66

The strings topic in the SO Documentation used to say, in the Remarks section:

Since C++14, instead of using "foo", it is recommended to use "foo"s, as s is a string literal, which converts the const char * "foo" to std::string "foo".

The only advantage I see using

std::string str = "foo"s;

instead of

std::string str = "foo";

is that in the first case the compiler can perform copy-elision (I think), which would be faster than the constructor call in the second case.

Nonetheless, this is (not yet) guaranteed, so the first one might also call a constructor, the copy constructor.

Ignoring cases where it is required to use std::string literals like

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

Is there any benefit of using std::string literals instead of const char[] literals?

Jarod42
  • 203,559
  • 14
  • 181
  • 302
Rakete1111
  • 47,013
  • 16
  • 123
  • 162
  • 3
    Errr... Does `auto` type deduction counts? The almost-always-auto advice has some controversy after all. –  Jul 28 '16 at 02:57
  • 1
    A lot of things in C++ are about semantics. The ideal is that you describe what you want done as good as possible, and let the compiler figure out everything else. However, don't over-do it so that the compiler will have room to breath (and optimize). – Ivan Rubinson Jul 28 '16 at 03:07
  • 3
    Consider the case where you pass a string literal for a parameter with a type that is constructible from `std::string`, but not from a C string. – chris Jul 28 '16 at 03:08
  • @NickyC No it doesn't :) – Rakete1111 Jul 28 '16 at 03:45
  • @chris Haven't thought about that, thanks – Rakete1111 Jul 28 '16 at 03:46
  • 1
    For c++17 in some cases [`string_view`](http://en.cppreference.com/w/cpp/string/basic_string_view) may be preferable to store literals, as it does not 'touch heap'. – Paul Rooney Jul 28 '16 at 03:55
  • @PaulRooney Good point :) – Rakete1111 Jul 28 '16 at 03:56
  • 2
    @PaulRooney, `string_view` is extremely useful, but keep in mind that most implementations of `std::string` don't either for short strings. – chris Jul 28 '16 at 04:01

5 Answers5

63

If you're part of the "Almost Always Auto" crowd, then the UDL is very important. It lets you do this:

auto str = "Foo"s;

And thus, str will be a genuine std::string, not a const char*. It therefore permits you to decide when to do which.

This is also important for auto return type deduction:

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

Or any form of type deduction, really:

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

foo("Foo"s);

The only advantage I see using [...] instead of [...] is that in the first case the compiler can perform copy-elision (I think), which would be faster than the constructor call in the second case.

Copy-elision is not faster than the constructor call. Either way, you're calling one of the object's constructors. The question is which one:

std::string str = "foo";

This will provoke a call to the constructor of std::string which takes a const char*. But since std::string has to copy the string into its own storage, it must get the length of the string to do so. And since it doesn't know the length, this constructor is forced to use strlen to get it (technically, char_traits<char>::length, but that's probably not going to be much faster).

By contrast:

std::string str = "foo"s;

This will use the UDL template that has this prototype:

string operator "" s(const char* str, size_t len);

See, the compiler knows the length of a string literal. So the UDL code is passed a pointer to the string and a size. And thus, it can call the std::string constructor that takes a const char* and a size_t. So there's no need for computing the string's length.

The advice in question is not for you to go around and convert every use of a literal into the s version. If you're fine with the limitations of an array of chars, use it. The advice is that, if you're going to store that literal in a std::string, it's best to get that done while it's still a literal and not a nebulous const char*.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 2
    Template function type deduction might be a good example as well. – Revolver_Ocelot Jul 28 '16 at 08:35
  • **−1** There is no problem doing `auto str = "Foo";`, so the claim that `"Foo"s` lets you do that is just nonsense. – Cheers and hth. - Alf Jul 28 '16 at 16:51
  • 4
    @Cheersandhth.-Alf: If you want a `std::string` rather than an array, then doing `auto str = "Foo";` is problematic. – Nicol Bolas Jul 28 '16 at 17:23
  • @NicolBolas: Your claim "it lets you do this" is nonsense also for a `std:.string` object declared with `auto`. E.g. you can declare it as `auto s = string( "blah" );`. Although it would be much better to do `std::string s = "blah";`. – Cheers and hth. - Alf Jul 28 '16 at 19:03
  • Note: Your nonsense claims do not become less nonsense by adding possible motivations. They just become *more* nonsense that way. – Cheers and hth. - Alf Jul 28 '16 at 19:05
  • 3
    1) Without the UDL it's a pointer, not an array. `auto` decays. 2) (ultra-pedantic): it calls `char_traits::length`, which may but isn't required to call `strlen`. – T.C. Jul 28 '16 at 20:08
  • @T.C: Sorry, but are you defending that nonsense? You have a very high rep score with me, and I think with most readers, re the technical, but this is misleading. I think you should take care to note that "it" in your point (2) doesn't refer to `auto s = "blah"`, and also note that the facts you note do not make his statement less nonsensical. – Cheers and hth. - Alf Jul 28 '16 at 20:19
  • @Cheersandhth.-Alf: 1: He's actually right. [Here's a live example.](http://ideone.com/bQdc47) – Nicol Bolas Jul 28 '16 at 20:25
  • @NicolBolas: He's right *and* your claims are utter nonsense. I haven't looked at your demonstration. We are discussing novice level things and you're out of your depth. – Cheers and hth. - Alf Jul 28 '16 at 20:26
  • @Cheersandhth.-Alf: "*Sorry, but are you defending that nonsense?*" No, unlike you, he was trying to *correct* me. That is, he was informing me about something I may not have known. – Nicol Bolas Jul 28 '16 at 20:27
  • Just a note, every compiler that is not 10 years old will optimize away that strlen call, as Nicol Bolas said himself, the compiler knows the size, so everytime it sees a std::strlen on a string literal, it will optimize it. So everyone: don't be fooled, this won't speed up anythng. – Gam Sep 01 '17 at 23:47
  • 1
    Does UDL mean user-defined literal? – Nubcake Mar 16 '22 at 14:35
  • @Gam so, you will easily be able to show a working demonstration, e.g. on godbolt, of the strlen being optimised away? – underscore_d Apr 17 '22 at 23:16
24

The advice to use "blah"s has nothing to do with efficiency and all to do with correctness for novice code.

C++ novices who don't have a background in C, tend to assume that "blah" results in an object of some reasonable string type. For example, so that one can write things like "blah" + 42, which works in many script languages. With "blah" + 42 in C++, however, one just incurs Undefined Behavior, addressing beyond the end of the character array.

But if that string literal is written as "blah"s then one instead gets a compilation error, which is much preferable.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
21

In addition, UDL makes it easier to have \0 in the string

std::string s = "foo\0bar"s; // s contains a \0 in its middle.
std::string s2 = "foo\0bar"; // equivalent to "foo"s
Rakete1111
  • 47,013
  • 16
  • 123
  • 162
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Regarding the link to presumed authority (since there's no more detailed info there): the SO documentation is not an authority, it's at the opposite end: completely untrustworthy. As it is per late July 2016. cppreference.com is a good resource to link to. – Cheers and hth. - Alf Jul 28 '16 at 19:53
2
  1. Using a C++ string literal means we do not need to call strlen to compute the length. The compiler already knows it.
  2. Might allow library implemetations where the string data points to memory in global space will using C literals must always force a copy of the data to heap memory on construction.
doron
  • 27,972
  • 12
  • 65
  • 103
  • 2
    COW strings are no longer allowed (since C++11), so I can't imagine how #2 would be possible. If `std::string` contains a string, it must own it, and do so uniquely. – Nicol Bolas Jul 28 '16 at 03:14
  • This is not COW, just using global space as the initial storage. – doron Jul 28 '16 at 03:32
  • 2
    If you're using global space as the initial storage, then it's very possible that more than one `std::string` is using that same global storage. In pretty much every compiler, if you use the same literal twice, it'll only be in the string table once. As such, if you try to modify that `string`, the object will have to copy it out to object-specific storage first. That is the essential essence of COW. – Nicol Bolas Jul 28 '16 at 03:45
  • @NicolBolas: I remember implementing a good part of `std::string`'s functionality as COW, for a question here on SO, just to prove that the statement that they're no longer allowed is incorrect. As I recall C++11 rules reduce the gains from COW so that it's now not practically desirable. – Cheers and hth. - Alf Jul 28 '16 at 16:45
  • @Cheersandhth.-Alf: It's hard to say if your implementation disproves my statement, since I can't see it. Spec behavior can be very subtle, and there's no way to know without seeing your code if you violated some element of it in your partial implementation. In particular, I'm curious as to how you implemented [basic.string]/5. – Nicol Bolas Jul 28 '16 at 16:50
  • @NicolBolas: The burden of proof lies on you, sir; I've already invested work. If you think the spec is too subtle to reason that out, fine. Then the claim doesn't hold. ;-) – Cheers and hth. - Alf Jul 28 '16 at 16:54
  • @Cheersandhth.-Alf: "*The burden of proof lies on you, sir; I've already invested work.*" But since you didn't link to it, I have no idea where it is, so I can't invest any such work. – Nicol Bolas Jul 28 '16 at 17:22
  • @NicolBolas: I meant, that for your claim, you have to back it up by reasonable facts and logic. You don't have to copy-and-modify anything I've written for that. – Cheers and hth. - Alf Jul 28 '16 at 19:00
  • @Cheersandhth.-Alf: "*I meant, that for your claim, you have to back it up by reasonable facts and logic. You don't have to copy-and-modify anything I've written for that.*" I don't have to. [You've apparently argued the point to death elsewhere](http://stackoverflow.com/q/12199710/734069). I don't feel like getting into such an argument, so I'll just say that I agree with Dave and Jonathon there. Consider their remarks mine. – Nicol Bolas Jul 28 '16 at 20:22
  • @NicolBolas: Dave's highly upvoted assertion is irrational, failing simple logic, disregarding any counter-example etc. But **Jonathon's point is good**, that the g++ (whatever it's called) buggy COW implementation isn't permitted. However, that was not your claim. – Cheers and hth. - Alf Jul 28 '16 at 20:34
  • 1
    @NicolBolas: I've done the proof job for you, because I found I had promised to update my (self-deleted for that reason) answer in the thread you linked. I just posted a [new answer](http://stackoverflow.com/a/38651410/464581). As it turns out, checking my own old arguments, C++11 indeed prohibits COW implementation of `basic_string`, via constant time requirements on item access. For some reason the other respondents failed to notice this. Also, 75 upvoters failed to notice that the logic in Dave's answer was entirely bogus, possibly because he arrived at the correct conclusion? – Cheers and hth. - Alf Jul 29 '16 at 05:59
0

This is old and already has wonderful answers. I just want to add another use case for the UDL for 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'
}

Just my two cents.

Costantino Grana
  • 3,132
  • 1
  • 15
  • 35