我应该将std::string与"string"或"string"s进行比较?

45

考虑以下代码片段:

bool foo(const std::string& s) {
    return s == "hello"; // comparing against a const char* literal
}

bool bar(const std::string& s) {
    return s == "hello"s; // comparing against a std::string literal
}
乍一看,与const char*进行比较似乎需要更少的汇编指令1,因为使用字符串字面值将导致std::string的原地构建。
(编辑:正如答案中指出的那样,我忘记了实际上在foo()中将调用s.compare(const char*),因此在这种情况下不会发生原地构建。因此划掉下面的一些行。)
然而,看着operator==(const char*, const std::string&)参考:

所有比较都通过compare()成员函数进行。

据我理解,这意味着我们将需要构造一个std::string来执行比较,因此我怀疑最终的开销将是相同的(虽然被operator==的调用隐藏)。
  • 我应该选择哪个比较?
  • 是否有一个版本比另一个版本更具优势(可能在特定情况下)?
1我知道较少的汇编指令并不一定意味着更快的代码,但我不想在这里进行微基准测试。

9
返回 s 是否等于 "hello"。 - molbdnilo
4
最好使用-O2/3进行比较,我的意思是谁会在调试版本中关心指令的数量呢?;) - 463035818_is_not_a_number
7
不是这样的。这是一种不写不必要浪费代码的情况。在字符串字面量和不必要的字符串实例化之间做出选择并不是微观优化;这是常识!换句话说,这确实与编写良好的代码有关。 - Lightness Races in Orbit
9
目前世界上存在太多臃肿慢速的代码,因为人们认为写出精简高效的代码会被视为“过早优化”,于是他们不再思考自己所编写的代码,这是一种令人遗憾的情况。 - Lightness Races in Orbit
4
@Someprogrammerdude 这与过早优化没有任何关系,我一开始只是出于好奇而询问,而且这会在某种程度上影响编程风格。你不应该仅仅因为他们不应该关心“过早优化”,就告诉人们在一般情况下使用 const Aconst A& - andreee
显示剩余3条评论
3个回答

67

不一定。

如果你想聪明一些,可以与"string"sv进行比较,它返回一个std::string_view


虽然与类似"string"的字面量比较不会产生任何分配开销,但它被视为以 null 结尾的字符串,具有所有相关缺点:不能容忍嵌入的 null,并且用户必须注意 null 结束符。

"string"s会进行分配,除了小字符串优化分配省略。此外,该操作符会传递字面量的长度,无需计数,并允许嵌入的 null。

最后,使用"string"sv结合了两种其他方法的优点,避免了它们各自的缺点。此外,std::string_viewstd::string更简单,特别是如果后者使用SSO,所有现代的字符串都会这样做。


至少从C++14开始(通常允许省略分配),理论上,编译器可以在有足够信息(在此示例中通常可用)和付出努力的情况下将所有选项优化到最后一个选项,在按照 as-if 规则的前提下。但我们还没有那么做。


3
是的,有这个函数。不幸的是,标准对于像这样的新重载使用非常晦涩的术语,没有太多好处(而不是传统的列出重载及其简单参数的方法)。但你要找的是这里的第7-9个函数:https://en.cppreference.com/w/cpp/string/basic_string/compare - Lightness Races in Orbit
6
抱歉,相较于简单的字符串字面值,这样做有什么好处?别误会,我喜欢字符串视图并且在合适的时候会进行谨慎使用,但是我在这里没有看到它的优势。 - Lightness Races in Orbit
7
实际上,使用 -O3 标志编译的 gcc 甚至能够内联调用 compare 函数(参见链接),而这在常规字符串字面量中是不会发生的。 - Cássio Renan
10
使用 operator "" sv 创建 string_view 时不存在 strlen() - Deduplicator
4
在使用gcc9.1 -Ofast优化选项时,使用string_view是最快的,因为它会内联调用比较函数。在使用-Os优化选项时,string_view和字符串字面量版本都会调用memcmp。请参考以下链接以获取更多信息:https://godbolt.org/z/jVUzSd。 - KamilCuk
显示剩余12条评论

15
不需要为const char*操作数构建std::string,compare()函数不要求这样做。你正在使用overload #4。与字符串字面量的比较是你要寻找的“免费”版本。在这里实例化std::string是完全不必要的。

1
只有当适当的“compare”函数的实现本身不创建“std :: string”对象时(尽管这可能非常不太可能),才能使用“Free”。 - Some programmer dude
5
在这个抽象程度上是免费的,是的。理论上,可以设计实现方法将比较操作移至云端,这可能会导致多毫秒的延迟和登录 Facebook,但让我们现实一点 ;) - Lightness Races in Orbit
(然而,这确实是我使用引号的原因,因为没有什么东西是真正免费的。) - Lightness Races in Orbit
1
好的,我明白了。_"与字符串字面值的比较是你要寻找的“免费”版本。"_:通过字符串字面值,您似乎在谈论普通的C字面量,而我则考虑了std::string_literals。这可能是问题所在 :-) - andreee
@andreee:是的,这是一个术语问题:“hello”在C和C++术语中都被称为“字符串字面值”。如果你指的是std::string字面值,你需要说“std::string字面值”,或者可能是带有代码格式的“string字面值”,但后者仍然可能会引起混淆。 - Peter Cordes
显示剩余7条评论

9
据我理解,这意味着我们需要构建一个std::string以执行比较,因此我怀疑最终的开销将是相同的(尽管被operator==调用隐藏了)。但这种推理是错误的。 std::compare不需要将其操作数分配为C风格的空字符结尾字符串即可运行。根据其中一个重载:
int compare( const CharT* s ) const; // (4)

4) 将此字符串与以s指向的字符为起点,长度为Traits::length(s)的空结尾字符序列进行比较。

虽然是否需要分配内存是一个实现细节,但是好像进行序列比较不应该这样做。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接