C++17 详解 20 — std::string_view
本文为 《C++17 in detail》 一书的中文渣中渣译文,不足之处还望指正。
翻译太特么累人了……剩余部分还是只做摘要翻译吧。
9. std::string_view
头文件:
1 |
string_view 是 string 的“视图”。它不拥有 string,也不会复制其内容,但你却可以通过 string_view 进行一些 string 相关的操作,从而避免不必要的复制。
比如:
1 | // string function: |
上边代码会执行至少三次字符串复制:
- str 定义时
- 函数构造第二个参数时:需要从 const char* 转换成 string 对象
- subStr 初始化
这种 substr 操作就很适合用 string_view 进行优化:
1 | std::string_view StartFromWord(std::string_view str, std::string_view word) |
上边代码只进行一次字符串的复制—— str 定义。string_view 通常只持有指向字符串起始位置的指针和长度,所以它是很轻量的。
译注:GCC 下 sizeof(string_view) = 16。所以再轻量,作为函数参数传递时该用引用还是得用!
std::basic_string_view 类型
同 string 一样,string_view 也是一系列模板类的简称:
1 | std::string_view std::basic_string_view<char> |
创建
- 从一个带终止符(\0)的 const char* 字符串创建
1 | const char* cstr = "Hello World"; |
- 从 const char* 和一个长度创建
1 | const char* cstr = "Hello World"; |
- 从 std::string 创建
1 | std::string str = "Hello String"; |
- 通过 “”sv 字面量创建
1 | using namespace std::literals; |
输出:
1 | Hello Super World, len: 18 |
data() 返回指向首字符的指针,所以 cout 输出时遇到终止符时即停止打印。
其它操作
一个大概总结:
既然 string_view 是对 string 的虚代理,那合理推测是 string_view 应提供同 string 相同的操作接口;又因 string_view 不持有字符串,只做视图用,那 string_view 应剔除修改相关(即非 const)接口。
对 string_view 自身属性的访问:
- remove_prefix(size_t n)
将 string_view 持有的指针向前移动 n 个位置(+n),string_view::size() 随之减小 n。n > string_view::size() 行为未定义。
- remove_suffix(size_t n)
将 string_view::size() 减小 n。n > string_view::size() 行为未定义。
- swap
交换两个 string_view 对象内容,即指针和尺寸。
风险
- data()
1 | std::string s = "Hello World"; |
如之前提到的,data() 返回的是起始位置的字符指针(const char*),以 data() 返回值进行打印输出很可能得不到你想要的结果。
进行基于字符串的转换时也有此问题:
1 | std::string number = "123.456"; |
你很可能期望 f 值等于 123,但实际 f = 123.456
- 引用对象生命周期
还是最开始时候的函数:
1 | std::string_view StartFromWord(std::string_view str, std::string_view word) |
但如果以临时对象调用会怎么样?
1 | auto str = "My Super"s; |
对 sv 的操作将导致运行时错误。
译注:
其实 string_view 跟智能指针的设计思路是一样的,都是享元设计模式的具体体现。
因为 string_view 处理的是字符串相关,在通常的引用对象生命周期问题外引入了字符串领域的空字符问题。这其实也不是一个新鲜问题,如果你有用 string 作为字节缓冲区(而非字符串)的经历,你一定也对 string::data() 中的坑不陌生。
性能 & 内存考量
string_view 通常以 [ptr, length] 结构实现,很轻量;
string_view 上的字符串操作同 string 中的同类操作具有一致的复杂度;
string_view 中的字符串操作函数绝大多数都是 constexpr 的:
1 |
|
如上,strv、strvCut 都是在编译期构造的。