Why Are String Literals L-Value While All Other Literals Are R-Value

rvalue definition is objects that cannot be assigned values, but why are literals lvalues?

There are three categories of non-assignable lvalues in C: const objects, arrays (and objects with members that are const or array) and expressions with incomplete type.

String literals are arrays (by which I mean: the object to which a string literal expression refers is an array). That makes them lvalues.

Informally, an lvalue is an expression that refers to an object, and a string literal is an object.


If you want the formal definition of an lvalue in C, refer to the standard. In C99(6.3.2.1):

An lvalue is an expression with an object type or an incomplete type
other than void; if an lvalue does not designate an object when it is evaluated,
the behavior is undefined.

with a footnote:

The name ‘‘lvalue’’ comes originally from the assignment expression E1
= E2,in which the left operand E1 is required to be a (modifiable) lvalue. It is perhaps better considered as representing an object
‘‘locator value’’

So appearing on the LHS of an assignment has nothing to do with it, other than the fact that lvalue used to mean something else, in some language other than standard C.

You might think that by that formal definition an integer constant 5 should also be an lvalue expression: it is an expression (specifically a primary-expression), and it has an object type (int is an object type). It's certainly valid to evaluate the expression 5, so if it's an lvalue then it must "designate an object".

This would contradict the definition of the address-of operator, which says that its operand may be an lvalue expression that designates an object. Compilers reject &5, generally with an error message saying that 5 is not an lvalue.

I think the answer lies in 6.5.1, which lists the different types of primary-expression, including constants. For each of the other kinds of primary-expression it says under what conditions it is an lvalue. For constants it conspicuously says nothing, indicating that they aren't lvalues. But usually in the C standard, text of the form "An X is...", with X italicised, is a definition of the term X. So I think the standard could be clearer.

Update: the text in n1570 (a late draft of C11) is, with my emphasis added:

An lvalue is an expression (with an object type other than void) that
potentially designates an object

And the equivalent text in C89 (3.2.2.1) says "that designates an object" [thanks hvd]. This suggests to me that the authors of the standard aren't happy with the text either. A constant doesn't potentially designate an object, so under this definition numeric constants certainly are not lvalues.

Function overload for string literals lvalue and rvalue reference

  1. Which compiler is correct ?

GCC is correct.


  1. With clang, why str1 and str2 choose the rvalue overload while they are lvalues ?

Clang is wrong on test(str1);, it should be ambiguous. For test(str2);, str2 could convert to pointer implicitly, i.e. the array-to-pointer decay. The converted char* is an rvalue. For the same reason as #3, the implicit conversion sequences have the same ranking, then non-template function is prefered; test(char*&&) is selected.


  1. With gcc, why call with str1 is ambiguous ?

For test(const char (&)[1]) to be called, qualification conversion from char[1] to const char[1] is required; for test(char*&&) to be called, array-to-pointer conversion is required. Both are qualified as exact match and have the same ranking.


  1. Is there a standard rule for this situation ?

See the ranking of implicit conversion sequences in overload resolution, and implicit conversions.


  1. How to fix the two last calls ?

It depends on your intent.

Why are literals and temporary variables not lvalues?

And also that literals and temporaries variables are not lvalues, but no reason is given for this statement.

This is true for all temporaries and literals except for string literals. Those are actually lvalues (which is explained below).

Is it because literals and temporaries variables do not have defined storage location? If yes, then where do they reside if not in memory?

Yes. The literal 2 doesn't actually exist; it is just a value in the source code. Since it's a value, not an object, it doesn't have to have any memory associated to it. It can be hard coded into the assembly that the compiler creates, or it could be put somewhere, but since it doesn't have to be, all you can do is treat it as a pure value, not an object.

There is an exemption though and that is string literals. Those actually have storage since a string literal is an array of const char[N]. You can take the address of a string literal and a string literal can decay into a pointer, so it is an lvalue, even though it doesn't have a name.

Temporaries are also rvalues. Even if they exist as objects, their storage location is ephemeral. They only last until the end of the full expression they are in. You are not allowed to take their address and they also do not have a name. They might not even exist: for instance, in

Foo a = Foo();

The Foo() can be removed and the code semantically transformed to

Foo a(); // you can't actually do this since it declares a function with that signature.

so now there isn't even a temporary object in the optimized code.

Are literal strings and function return values lvalues or rvalues?

  1. string literals are lvalues, but you can't change them
  2. rvalue, but if it's a pointer and non-NULL, the object it points to is an lvalue

The C standard recognizes the original terms stood for left and right as in L = R; however, it says to think of lvalue as locator value, which roughly means you can get the address of an object and therefore that object has a location. (See 6.3.2.1 in C99.)

By the same token, the standard has abandoned the term rvalue, and just uses "the value of an expression", which is practically everything, including literals such as ints, chars, floats, etc. Additionally, anything you can do with an rvalue can be done with an lvalue too, so you can think of all lvalues as being rvalues.

Since a string literal is considered an lvalue, why must the binding lvalue reference be const?

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.

Why string literal is not prvalue

String literals cannot be moved from. They're arrays of fundamental types, so a move would be indistinguishable from a copy.

And string literals have de-facto object identity since they have a lifetime that exceeds their local scope (another reason they cannot be moved from). That's why you can return a const char* of a literal from a function and still have the program work. Also, two string literals can refer to the same array of characters (their pointers can be identical) if the literals are of the same string. So "bar" and "bar" may point to the same memory.



Related Topics



Leave a reply



Submit