16

I know there are topics that are similar to this one already (such as this).

The example given in this topic was this:

std::string & rs1 = std::string();

Clearly, that std::string() is an rvalue. However, my question is why is s1 legal while s2 is not?

const std::string& s1 = "String literal";
std::string& s2 = "String literal";

The standard clearly states that string literals are lvalues (which is understandable since they are technically const char* behind the scenes). When I compile s2 though, I get the following:

prog.cpp:4:19: error: invalid initialization of non-const reference of type
'std::string& {aka std::basic_string<char>&}' from an rvalue of type
'const char*' std::string& s2 = "String literal";

I understand that the standard definition of lvalues and rvalues are mutually exclusive, so is this potentially an error with the compiler? I'm using gcc 4.9.2 in this example. Would this also be one of the cases where the literal is really an xvalue?

Community
  • 1
  • 1
elau
  • 179
  • 2
  • 7
  • Because non-const lvalue references cannot bind to temporaries. const lvalues references can. A temporary object of type string is created from "const char*". This is bound to the reference. – bolov Jan 09 '15 at 21:46
  • Are you sure your example code is what you compiled? `const std::string & s2 = "String literal"` is totally valid, whereas, obviously `std::string & s1 = "String literal"` is not. – mbgda Jan 09 '15 at 21:46
  • Edited it. Thanks for the quick reply. I just realized I copied/pasted wrong. – elau Jan 09 '15 at 21:48
  • 2
    A "string literal" is a lexeme in the language grammar, not an object. The object denoted by the lexeme is a character array, not an object of type `std::string` (which is something from the library and entirely different). – Kerrek SB Jan 09 '15 at 21:51
  • 3
    @KerrekSB That seems to be vague. The standard specifies that "The effect of attempting to modify a string literal is undefined.", which implies that in some contexts the string literal *is* the character array. – Columbo Jan 09 '15 at 22:00
  • @Columbo I think that language is to allow for literals being stored in read-only memory, which might generate a trap when written to. – Mark Ransom Jan 09 '15 at 22:05
  • @MarkRansom I am not on about the fact that string literals are not to be modified, which is fine and good, but about the fact that the word string literal is used in this sentence. – Columbo Jan 09 '15 at 22:06
  • @Columbo: I only wanted to address the OP's apparent confusion between the multiple meanings of the word "string". – Kerrek SB Jan 09 '15 at 22:06
  • Also note that `const char (&s)[15] = "String literal";` will work, and that trying to remove the `const` qualifier doesn't really make any sense in that context. – cartographer Jan 10 '15 at 03:10

3 Answers3

22

The problem is that a string literal is not of type std::string or subclass thereof - it is of type
char const[N]. Thus the type of the initializer is not reference compatible with the target type of the reference, and a temporary must be created and bound to the reference.

However, temporaries cannot be bound to non-const lvalue references. I.e. your case is equivalent to

std::string& s = std::string("Abcdefg");

which is, even according to you, clearly ill-formed.


Actually the precise reason it doesn't work is not because temporaries cannot be bound to non-const lvalue references, but rather that the initializer of a non-const lvalue reference is subject to certain requirements that char const[N] cannot meet in this case, [dcl.init.ref]/5:

A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

  • If the reference is an lvalue reference and the initializer expression

    • is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2” or
    • has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be implicitly converted to an lvalue of type “cv3 T3,” where “cv1 T1” is reference-compatible with “cv3 T3106(this conversion is selected by enumerating the applicable conversion functions (13.3.1.6) and choosing the best one through overload resolution (13.3)),

    then the reference is bound to the initializer expression lvalue in the first case and to the lvalue result of the conversion in the second case (or, in either case, to the appropriate base class subobject of the object).

  • Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.

    • [..]

106) This requires a conversion function (12.3.2) returning a reference type.

Columbo
  • 60,038
  • 8
  • 155
  • 203
10

The string literal may be an lvalue, but it's not a string object. There's a temporary string being created which is an rvalue.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 2
    To expound on this, `s1` takes advantage of the rule that binding a const reference to a temporary extends the lifetime of the temporary to the scope of the reference, while binding a non-const reference to a temporary is an error. – tmyklebu Jan 09 '15 at 21:51
2

First, a string is a const char * or const char [N] not a std::string. So you're not directly assigning those char * strings. std::string has a constructor that takes a const char [N] and the compiler automatically uses it to construct a new instance.

But when using const for s2 you've just made it impossible for the compiler to assign the new std::string instance to s2.

So in the end I think you've misunderstood the difference between a "string" of type const char [N] and a "string" of type std::string. The standard is referring to const char [N] when it is talking about strings being an lvalue but you're attempting to apply that to a std::string to which the standard's rule doesn't apply.

erapert
  • 1,383
  • 11
  • 15