Tip of the Week #1: string_view

Posted zhangyifei216

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tip of the Week #1: string_view相关的知识,希望对你有一定的参考价值。

Tip of the Week #1: string_view

Originally published as totw/1 on 2012-04-20
By Michael Chastain (mec.desktop@gmail.com)
Updated 2017-09-18

What’s a string_view, and Why Should You Care?

当我们创建一个函数并且这个函数有一个参数是用来接收常量字符串的,这时你有四个选择,其中有两个是你已经知道的,另外两个你或许还不知道:

void TakesCharStar(const char* s);             // C convention
void TakesString(const string& s);             // Old Standard C++ convention
void TakesStringView(absl::string_view s);     // Abseil C++ convention
void TakesStringView(std::string_view s);      // C++17 C++ convention

前两种方式最适合那种调用者提供参数和行参的类型是一致的,但是如果不一致是否需要进行转换(可能是const char*转换为string或者是sgtring转换为const char*)?

调用者需要将string转换为const char*是需要使用(高效但是不方便)c_str()函数:

void AlreadyHasString(const string& s) 
  TakesCharStar(s.c_str());               // explicit conversion

调用者需要将一个const char*转换为一个string的时候是不需要做任何额外事情的(这是一个好消息),但是这会导致编译器会隐式的创建一个临时的字符串(方便但是不高效),然后将const char*中的内容拷贝到这个string中(这是个不好的消息):

void AlreadyHasCharStar(const char* s) 
  TakesString(s); // compiler will make a copy

What to Do?

谷歌的首选是通过string_view的方式来接收字符串参数,在C++17中可以使用std::string_view来替代,在那些还没有使用C++17的代码中可以使用absl::string_view来替代。

一个string_view类的实例可以被认为是一个对现存的字符bufer的视图,具体来说string_view仅仅包含了一个指针和一个长度,仅用来标识一段字符数据并不拥有这段数据,不能通过这个视图来修改对应的数据。所以对一个string_view的拷贝是浅拷贝,是没有字符串数据的拷贝的。

string_view可以隐式的从一个const char*const string&构造出来,并且不会造成字符串数据的拷贝产生O(n)的复杂度,例如当传递一个const string&的时候,构造函数仅会产生O(1)的复杂度,当传递一个const char*的时候构造函数会自动的调用一个strlen函数(或者你可以传递两个参数给string_view)。

void AlreadyHasString(const string& s) 
  TakesStringView(s); // no explicit conversion; convenient!



void AlreadyHasCharStar(const char* s) 
  TakesStringView(s); // no copy; efficient!

因为string_view不拥有数据,所以被string_view所指向的字符串(类似于const char*)都必须有一个已知的生命周期,而且其生命周期要长于string_view对象自己。这意味着使用string_view来存储字符串通常是有问题的,你需要证明你引用的底层数据其生命周期要长于string_view对象自己的。

如果你的API仅仅是需要在一个单次调用中引用一个字符串数据,而且不会修改数据,那么接收一个string_view作为参数这是足够的。如果你还需要在调用后还引用这个数据,或者修复这个数据,那么你需要显示通过string(my_string_view)来将其转换string类型。

string_view添加到现存的代码中并不总是正确的,如果传递给这个函数的需要的就是一个string或者是一个const char*使用string_view并不会很高效。最佳的方案是从工具代码开始向上开始采用string_view来替换,或者是从一个新项目一开始就保持一致。

A Few Additional Notes

  • 和其他字符串类型不一样的点在于string_view传递的时候应该按照值拷贝的方式传递,就像传递intdouble类型一样,因为string_view就是一个小数值。
  • string_view不需要以NULL结尾,因此如果这样输出是不安全的:
printf("%s\\n", sv.data()); // DONT DO THIS

然后,下面这样是可以的:
c++
printf("%.*s\\n", static_cast<int>(sv.size()), sv.data());

  • 你应该像一个string或者const char*那样来输出string_view
std::cout << "Took '" << s << "'";
  • 在大多数情况下你将安全的将一个现存的接收const string&const char*参数的函数转换为string_view我们在执行此操作时遇到的唯一危险是,如果函数的地址已被占用,则会导致构建中断,因为所产生的函数指针类型将有所不同

以上是关于Tip of the Week #1: string_view的主要内容,如果未能解决你的问题,请参考以下文章

Tip of the Week #59: Joining Tuples

Tip of the Week #11: Return Policy

Tip of the Week #64: Raw String Literals

Tip of the Week #64: Raw String Literals

Tip of the Week #36: New Join API

Tip of the Week #61: Default Member Initializers