std::string_view
已使用C ++ 17,因此广泛建议使用它代替 const std::string&
。
原因之一是性能。
有人能解释一下究竟 std::string_view
是/将快于const std::string&
作为参数类型时?(假设未在被调用方中进行任何复制)
std::string
(string_view可以接受原始数组,向量,std::basic_string<>
使用非默认分配器等等,等等等等哦,和其他string_views明显)
std::string_view
已使用C ++ 17,因此广泛建议使用它代替 const std::string&
。
原因之一是性能。
有人能解释一下究竟 std::string_view
是/将快于const std::string&
作为参数类型时?(假设未在被调用方中进行任何复制)
std::string
(string_view可以接受原始数组,向量,std::basic_string<>
使用非默认分配器等等,等等等等哦,和其他string_views明显)
Answers:
std::string_view
在某些情况下更快。
首先,std::string const&
要求数据位于std::string
而不是原始C数组中,而不是char const*
C API返回的数据,std::vector<char>
由反序列化引擎生成的数据,等等。避免的格式转换避免了复制字节,并且(如果字符串长于特定std::string
实现的SBO¹ )避免了内存分配。
void foo( std::string_view bob ) {
std::cout << bob << "\n";
}
int main(int argc, char const*const* argv) {
foo( "This is a string long enough to avoid the std::string SBO" );
if (argc > 1)
foo( argv[1] );
}
在这种string_view
情况下,不会进行任何分配,但是如果使用foo
a std::string const&
而不是astring_view
。
第二个很大的原因是它允许使用子字符串而不使用副本。假设您正在解析2 GB的json字符串(!)²。如果将其解析为std::string
,则每个这样的解析节点将在其中存储节点名称或值的副本原始数据从2 gb字符串到本地节点。
相反,如果将其解析为std::string_view
,则节点将引用原始数据。这样可以节省数百万的分配,并且在解析过程中可以减少一半的内存需求。
您可以获得的加速速度简直是荒谬的。
这是一个极端的情况,但是其他“获取子字符串并使用它”情况也可以产生不错的加速 string_view
。
决定的重要部分是您因使用而损失了什么 std::string_view
。数量不多,但确实如此。
您将丢失隐式空终止,仅此而已。因此,如果将相同的字符串传递给3个函数,而所有这些函数都需要一个空终止符,std::string
则明智的做法是将其转换为一次。因此,如果已知您的代码需要空终止符,并且您不希望从C样式的源缓冲区等提供的字符串,则可以使用std::string const&
。否则采取行动std::string_view
。
如果std::string_view
有一个标志说明它是否为null终止(或更奇特的东西),它将删除使用的最后一个原因std::string const&
。
在某些情况下,对a std::string
采取不采取行动const&
是最优的std::string_view
。如果您需要在调用后无限期拥有该字符串的副本,则按值取值是有效的。您要么处于SBO情况(没有分配,仅复制几个字符),要么就可以将堆分配的缓冲区移动到local std::string
。具有两个重载,std::string&&
并且std::string_view
可能更快,但是只有一点点,这将导致适度的代码膨胀(这可能会浪费您所有的速度提升)。
¹小缓冲区优化
²实际用例。
string_view改善性能的一种方法是,它允许轻松删除前缀和后缀。在幕后,string_view可以仅将前缀大小添加到指向某个字符串缓冲区的指针,或者从字节计数器中减去后缀大小,这通常很快。另一方面,当您执行诸如substr之类的操作时,std :: string必须复制其字节(通过这种方式,您将获得拥有其缓冲区的新字符串,但在许多情况下,您只想获取原始字符串的一部分而不进行复制)。例:
std::string str{"foobar"};
auto bar = str.substr(3);
assert(bar == "bar");
使用std :: string_view:
std::string str{"foobar"};
std::string_view bar{str.c_str(), str.size()};
bar.remove_prefix(3);
assert(bar == "bar");
我写了一个非常简单的基准来添加一些实数。我使用了很棒的Google基准测试库。基准功能包括:
string remove_prefix(const string &str) {
return str.substr(3);
}
string_view remove_prefix(string_view str) {
str.remove_prefix(3);
return str;
}
static void BM_remove_prefix_string(benchmark::State& state) {
std::string example{"asfaghdfgsghasfasg3423rfgasdg"};
while (state.KeepRunning()) {
auto res = remove_prefix(example);
// auto res = remove_prefix(string_view(example)); for string_view
if (res != "aghdfgsghasfasg3423rfgasdg") {
throw std::runtime_error("bad op");
}
}
}
// BM_remove_prefix_string_view is similar, I skipped it to keep the post short
(x86_64 linux,gcc 6.2,“ -O3 -DNDEBUG
”):
Benchmark Time CPU Iterations
-------------------------------------------------------------------
BM_remove_prefix_string 90 ns 90 ns 7740626
BM_remove_prefix_string_view 6 ns 6 ns 120468514
主要有两个原因:
string_view
是现有缓冲区中的一个片,它不需要内存分配string_view
通过值而不是通过引用传递切片的优点有很多:
char const*
或char[]
在分配不分配新缓冲区的情况下始终具有更好,更一致的性能。
与别名一样,按值传递也比按引用传递具有优势。
具体来说,当您有 std::string const&
参数时,无法保证不会修改参考字符串。结果,在每次调用不透明方法(指向数据,长度等)之后,编译器必须重新获取字符串的内容。
另一方面,当string_view
按值传递值时,编译器可以静态确定当前没有其他代码可以修改堆栈(或寄存器)中的长度和数据指针。结果,它可以跨函数调用“缓存”它们。
它可以做的一件事是避免std::string
在从空终止字符串进行隐式转换的情况下构造对象:
void foo(const std::string& s);
...
foo("hello, world!"); // std::string object created, possible dynamic allocation.
char msg[] = "good morning!";
foo(msg); // std::string object created, possible dynamic allocation.
const std::string str{"goodbye!"}; foo(str);
可能不会比使用string&快
string_view
很慢,因为它必须复制两个指针而不是一个指针const string&
?
std::string_view
基本上只是一个包装纸const char*
。并且passing const char*
表示与passing const string*
(或const string&
)相比,系统中的指针要少string*
一些,因为这意味着:
string* -> char* -> char[]
| string |
显然,为了传递const参数,第一个指针是多余的。
psstd::string_view
和之间的一个const char*
重大区别是,string_views不需要以空值结尾(它们具有内置的大小),并且允许对更长的字符串进行随机就地拼接。
std::string_view
s只是花哨的const char*
s,时期。GCC的实施方式如下:class basic_string_view {const _CharT* _M_str; size_t _M_len;}
std::string const*
。该图是难以理解的。//@n.caillou:您自己的评论已经比答案更准确。这string_view
远远超过了“花哨char const*
”-这确实很明显。
std::string const*
和std::string const&
是一样的,不是吗?
std::string_view
只是(char * begin,char * end)对的抽象。在制作std::string
不必要的副本时使用它。