C++17 string_view

string_view

C++17的标准中,string_view是一个比较惊喜的东西,有机会用于字符串相关操作的优化。string_view的设计思路类似于slice,通过片段应用减少buffer的复制。

本质上讲是对char数据的引用

1
The class template basic_string_view describes an object that can refer to a constant contiguous sequence of char-like objects with the first element of the sequence at position zero.

典型的实现仅包好两个成员:指向常量CharT的指针和长度。也就是说string_view本身不具有数据的拷贝。

string_view的操作函数与string基本一致。不过starts_with和ends_with这两个函数要等到C++20的时候才会实现。

string_view最大的优势就是对字符串的操作效率极高,一下代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <iostream>
#include <string>
#include <string_view>

#include <ctime>
#include <chrono>
using namespace std::literals;
using namespace std;

int main()
{
constexpr auto s = "it is a test"sv;
auto s1 = "it is a test"sv;
const char * cc = "it is a test";
string_view s2(cc, 12);
auto str = "it is a test"s;
const string str1 = "it is a test";
string_view s3(&str[0], str.size());
string_view s4(&str1[0], str1.size());
auto s5 = "it is a test"sv;
const auto s6 = "it is a test"sv;
constexpr int LEN = 1000000;

chrono::steady_clock::time_point st1 = chrono::steady_clock::now();
for (int i = 0; i < LEN; i++) {
constexpr auto st = s.substr(3);
}
chrono::steady_clock::time_point ed1 = chrono::steady_clock::now();

chrono::steady_clock::time_point st2 = chrono::steady_clock::now();
for (int i = 0; i < LEN; i++) {
auto st = s1.substr(3);
}
chrono::steady_clock::time_point ed2 = chrono::steady_clock::now();

chrono::steady_clock::time_point st3 = chrono::steady_clock::now();
for (int i = 0; i < LEN; i++) {
auto st = str.substr(3);
}
chrono::steady_clock::time_point ed3 = chrono::steady_clock::now();

chrono::steady_clock::time_point st4 = chrono::steady_clock::now();
for (int i = 0; i < LEN; i++) {
const auto st = str1.substr(3);
}
chrono::steady_clock::time_point ed4 = chrono::steady_clock::now();

chrono::steady_clock::time_point st5 = chrono::steady_clock::now();
for (int i = 0; i < LEN; i++) {
auto st = s2.substr(3);
}
chrono::steady_clock::time_point ed5 = chrono::steady_clock::now();

chrono::steady_clock::time_point st6 = chrono::steady_clock::now();
for (int i = 0; i < LEN; i++) {
auto st = s3.substr(3);
}
chrono::steady_clock::time_point ed6 = chrono::steady_clock::now();

chrono::steady_clock::time_point st7 = chrono::steady_clock::now();
for (int i = 0; i < LEN; i++) {
auto st = s4.substr(3);
}
chrono::steady_clock::time_point ed7 = chrono::steady_clock::now();

chrono::steady_clock::time_point st8 = chrono::steady_clock::now();
for (int i = 0; i < LEN; i++) {
auto st = s5.substr(3);
}
chrono::steady_clock::time_point ed8 = chrono::steady_clock::now();

chrono::steady_clock::time_point st9 = chrono::steady_clock::now();
for (int i = 0; i < LEN; i++) {
const auto st = s6.substr(3);
}
chrono::steady_clock::time_point ed9 = chrono::steady_clock::now();

cout << "constexpr sv time " << chrono::duration_cast<chrono::microseconds>(ed1 - st1).count() << "us.\n";

cout << "sv time " << chrono::duration_cast<chrono::microseconds>(ed2 - st2).count() << "us.\n";

cout << "s time " << chrono::duration_cast<chrono::microseconds>(ed3 - st3).count() << "us.\n";

cout << "const s time " << chrono::duration_cast<chrono::microseconds>(ed4 - st4).count() << "us.\n";

cout << "sv-char time " << chrono::duration_cast<chrono::microseconds>(ed5 - st5).count() << "us.\n";

cout << "sv-s time " << chrono::duration_cast<chrono::microseconds>(ed6 - st6).count() << "us.\n";

cout << "sv-const s s time " << chrono::duration_cast<chrono::microseconds>(ed7 - st7).count() << "us.\n";

cout << "sv time " << chrono::duration_cast<chrono::microseconds>(ed8 - st8).count() << "us.\n";

cout << "const sv time " << chrono::duration_cast<chrono::microseconds>(ed9 - st9).count() << "us.\n";
}

在GCC7.1 C++17下的执行结果

1
2
3
4
5
6
7
8
9
constexpr sv time 0us.
sv time 0us.
s time 9036us.
const s time 9478us.
sv-char time 0us.
sv-s time 563us.
sv-const s s time 543us.
sv time 0us.
const sv time 0us.

可以看到string_view操作字面量的话基本不消耗时间,而对于引用string变量进行操作减少了将近20倍的耗时,可以说效果拔群。

不过当前clang对于string_view的支持似乎还没有跟上,基本与string一致。不同版本的GCC差距也比较明显,要使用的话还是要考虑编译环境问题。