RaGa__M
RaGa__M

Reputation: 2569

Why string literal is not prvalue

I've been poking around with the value category just for this time.I am ok with all the definitions and the explanations that has given about categories,yet I feel it's better if someone can explain me why string literals are not a prvale

I've understood an expression's value/result has fallen into prvalue category when it doesn't has an identity but movable

int i=42;char i='a'; //prvalue
string i ="notprvalue";

doesn't the literal "notprvalue" has no identity and movable property?

Upvotes: 2

Views: 614

Answers (2)

Daniel Russell
Daniel Russell

Reputation: 769

String literals are an illusion. They seem like fundamental types, like an int or a char or pointer are. But they are not.

In general, they cannot be directly loaded into a register for example. A pointer to them can be of course. But they are not pointers either. They are arrays, which internally are implemented as pointers and can be decayed into pointers, but arrays are more than that, e.g. they track size for the sizeof operator. Arrays are not fundamental types.

The type of a C string literal is not char const* or even char *-- but rather char const[] in C++ (or just char[] in C). If this were not the case, the sizeof operator wouldn't work correctly on a string literal: sizeof "Lorem ipsum dolor sit amet" is 26, not 8. Pointers, have a constant size on modern systems: 4 or 8 bytes (depending on whether a system is 32 or 64 bits). That's not what you want when you do sizeof(my_string_literal).

So instead, string literals are arrays of char that are anonymous but you can magically refer to them by the literals themselves. It might not be technically correct, but I like to think of the literal itself as a sort of "identity" (after hashing of course) for the actual character array that is stored in the data segment of the program.

You can also create a writeable array of char and then when you initialize it with a string literal: char my_string[] = "my string literal", what you are actually doing is copying the elements of the anonymous char const[] into a new char[].

Are string literals actually constant? In C++ yes. But they are not implemented officially as such in C but modern compilers do not have to respect your attempts to modify them and so it's undefined behavior to do: char* my_string_pointer = "some literal"; my_string_pointer[5] = 'k'; But it's perfectly fine to do char[] my_string_array = "some literal"; my_strig_array[5] = 'k'. The former initializes a pointer. The latter initializes an array via copy.

So string literals are lvalues that you should treat as const even if they are not technically implemented as const. And it's undefined behavior to do otherwise.

The key thing to understand all this is that rvalues are not necssarily fundamental literal types. They can also be non-fundamental, non-literal temporaries, like so: int answer = my_class().compute_something() for struct my_class{ int data; int compute_someting(){ // ... compute ... } };

In this code, an instance of my_class is created temporarily to evaluate the expression where it occurs. It's lifetime would be extended by creating an lvalue identity to it. But since we don't do that, it's lifetime is just the statement where it occurs. It's dead by the end of the line of code containing it. Therefore it is an rvalue. Now, implementation-wise, it's like a short lived anonymous lvalue when it gets "materialized" in the expression. And this is why you should be able to move from it if you're initializing or assigning it to another lvalue that can do so. So some rvalues are in fact suspiciously like lvalues, but anonymous and shortlived. And it was this insight that led to "xvalues" and move semantics: the ability to manually mark an lvalue as a temporary when we are done with it, so we can move from it, by casting it with std::move.

By the way, prvalues can be thought of as "pure rvalues" in the sense that they are not lvalues that got casted with std::move to become rvalues, but just always were rvalues from the start, i.e. temporaries. A string literal is not a temporary, so it is not a prvalue. And string literals are not xvalues either because they live in the data segment of the program permanently from before the program ever is even run--on disk.

See Keith Thompson's answer about modifying string literals for more insight: Modifying String Literal

Upvotes: 1

Nicol Bolas
Nicol Bolas

Reputation: 474446

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.

Upvotes: 5

Related Questions