使用用户定义字面值字符串的优点,相较于字符��字面值

66

在SO文档的字符串主题中,备注部分曾经说:

自从C++14以来,建议使用"foo"s代替"foo",因为s是一个字符串常量,它将将const char *类型的"foo"转换为std::string类型的"foo"

我认为使用此方法唯一的优势是

std::string str = "foo"s;

取代

std::string str = "foo";

第一种情况下编译器可以进行复制省略(我想),比第二种情况中的构造函数调用更快。

尽管如此,这(目前)不能保证,因此第一个也可能调用构造函数,即复制构造函数。

忽略必须使用像std :: string文字的情况。

std::string str = "Hello "s + "World!"s;

使用std::string字面量比使用const char[]字面量有什么好处吗?


3
“auto”类型推导算吗?毕竟,“almost-always-auto”的建议引起了一些争议。 - user2486888
1
在C++中,很多东西都是关于语义的。理想情况下,您应该尽可能清楚地描述您想要完成的任务,并让编译器解决其他所有问题。但是,不要过度这样做,以便编译器有空间进行优化。 - Ivan Rubinson
3
考虑这样一种情况,您将一个字符串字面量作为参数传递给一个可以从std::string构造但不能从C字符串构造的类型。 - chris
1
对于C++17,在某些情况下,string_view可能更适合存储字面量,因为它不会“触及堆”。 - Paul Rooney
2
@PaulRooney,string_view非常有用,但请记住,大多数std::string的实现对于短字符串也不是最优选择。 - chris
显示剩余3条评论
5个回答

63

如果你是“几乎总是自动”派别的一员,那么UDL非常重要。它使你可以做到这一点:

auto str = "Foo"s;

因此,str 将是一个真正的 std::string,而不是一个 const char*。这使您可以决定何时执行哪个操作。

对于自动返回类型推导来说,这也非常重要:

[]() {return "Foo"s;}

或者任何形式的类型推导,确实如此:

template<typename T>
void foo(T &&t) {...}

foo("Foo"s);

我认为使用 [...] 而不是 [...] 的唯一优点是:在第一种情况下,编译器可以执行复制省略(我认为),这比第二种情况中的构造函数调用更快。 复制省略并不比构造函数调用更快。无论哪种方式,您都会调用对象的一个构造函数。问题是调用哪一个
std::string str = "foo";

这将引发对取一个const char*std::string构造函数的调用。但是,由于std::string必须将字符串复制到自己的存储中,因此它必须获取字符串的长度才能执行此操作。由于它不知道长度,因此该构造函数被迫使用strlen来获取长度(技术上是char_traits<char>::length,但这可能不会更快)。
相比之下:
std::string str = "foo"s;

这将使用具有以下原型的UDL模板:
string operator "" s(const char* str, size_t len);

看,编译器知道字符串字面量的长度。因此,UDL代码传递了一个指向字符串的指针和一个大小。因此,它可以调用带有const char*size_t参数的std::string构造函数。因此,不需要计算字符串的长度。
这里提到的建议并不是让你去转换每个使用字面量的地方为s版本。如果你对于一个char数组的限制感到满意,请使用它。建议是,如果你要将该字面量存储在std::string中,最好在它仍然是字面量而不是模糊的const char*时完成。

2
模板函数类型推导也可能是一个很好的例子。 - Revolver_Ocelot
4
如果您想要一个std::string而不是一个数组,那么使用auto str = "Foo";会有问题。 - Nicol Bolas
3
  1. 没有使用UDL时,它是一个指针而不是数组。auto会退化为指针。
  2. (极端严谨)它调用char_traits<char>::length,这可能但不一定会调用strlen
- T.C.
只是一点提醒,任何不是10年前的编译器都会优化掉那个strlen调用,正如Nicol Bolas自己所说,编译器知道大小,所以每次看到一个字符串字面量上的std::strlen,它都会进行优化。所以大家不要被愚弄了,这不会加速任何东西。 - Gam
1
UDL 是指用户定义字面量吗? - Nubcake
显示剩余8条评论

24

建议使用"blah"s与效率无关,而是为了新手代码的正确性。

没有C背景的C++新手往往会认为"blah"会产生某种合理的字符串类型的对象。例如,这样就可以编写像"blah" + 42这样的语句,在许多脚本语言中这是可行的。然而,在C++中使用"blah" + 42会导致未定义行为,超出字符数组的末尾。

但是,如果将该字符串字面值写成"blah"s,那么将得到一个编译错误,这是更可取的。


21

此外,UDL使字符串中的\0更容易处理。

std::string s = "foo\0bar"s; // s contains a \0 in its middle.
std::string s2 = "foo\0bar"; // equivalent to "foo"s

关于指向假定权威的链接(因为那里没有更详细的信息):SO文档不是权威,相反,它是完全不可信的。截至2016年7月底。cppreference.com是一个好的链接资源。 - Cheers and hth. - Alf

2
  1. 使用C++字符串字面量意味着我们不需要调用strlen来计算长度。编译器已经知道了。
  2. 可能允许库实现,其中字符串数据指向全局空间中的内存,而使用C语言字面量必须始终在构建时将数据复制到堆内存中。

2
COW字符串自C++11起不再被允许,因此我无法想象第二种情况是如何可能的。如果std::string包含一个字符串,它必须拥有它,并且独占地拥有它。 - Nicol Bolas
这不是COW,只是使用全局空间作为初始存储。 - doron
2
如果您使用全局空间作为初始存储,则很可能会有多个std::string使用相同的全局存储。在几乎所有编译器中,如果您两次使用相同的文字,它只会在字符串表中出现一次。因此,如果您尝试修改该“字符串”,则对象首先必须将其复制到特定于对象的存储中。这就是COW的本质要义。 - Nicol Bolas
@NicolBolas:我记得在Stack Overflow上回答一个问题时,实现了std::string的很大一部分功能作为COW,只是为了证明它们不再被允许的说法是不正确的。据我所知,C++11规则减少了COW的收益,因此现在不再实用。 - Cheers and hth. - Alf
1
@NicolBolas:我已经为您完成了证明工作,因为我发现我曾承诺在您链接的线程中更新我的(出于这个原因而自删的)答案。我刚发布了一个新答案。事实证明,在检查自己旧的参数时,C++11确实禁止了对basic_string的COW实现,因为它要求在常量时间内访问每个项目。由于某种原因,其他回答者没有注意到这一点。此外,75名投票者没有注意到Dave答案中的逻辑完全错误,可能是因为他得出了正确的结论? - Cheers and hth. - Alf
显示剩余6条评论

1

这是旧的问题,已经有很好的答案了。我只想为 std::string 添加另一个使用案例:

for (char cur : "abcdefghijklR") {
    // this will loop from 'a' to 'R' and add another loop with cur=0
}

for (char cur : "abcdefghijklR"s) {
    // this will loop from 'a' to 'R'
}

这只是我的个人意见。


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