2

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?

RaGa__M
  • 2,550
  • 1
  • 23
  • 44

2 Answers2

5

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.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • when you say fundamental types you meant `built-in types`? – RaGa__M Dec 27 '16 at 18:40
  • @FallingFromBed: The standard calls them "fundamental types". – Lightness Races in Orbit Dec 27 '16 at 18:55
  • @LightnessRacesinOrbit, ok, When we say `int i=42` the expression `42` which is of fundamental type consider as `prvalue` I believe the fundamental types aren't movable as it lacks move ctor/assignment, Doesn't it violate the` prvalue` property? The `42` still should come under `prvalue`? – RaGa__M Dec 27 '16 at 19:02
  • 1
    @FallingFromBed: Being a _prvalue_ doesn't mean "I am movable". But, yes, the integer literal `42` is a _prvalue_. – Lightness Races in Orbit Dec 27 '16 at 19:10
  • @LightnessRacesinOrbit, then `prvalue`= has no identity or movable? – RaGa__M Dec 27 '16 at 19:12
  • @FallingFromBed: I don't really understand what you're asking me. Roughly speaking, _prvalue_ expressions tend to be those that evaluate to objects with no name. But there are exceptions; string literals are _lvalues_. You can't really come up with a global, single rule; the definitions given in 3.10/1 are as close as you can get. – Lightness Races in Orbit Dec 27 '16 at 19:12
  • @LightnessRacesinOrbit, " prvalue expressions tend to be those that evaluate to objects with no name" so we can take the movable property as just an option ? – RaGa__M Dec 27 '16 at 19:15
  • @FallingFromBed: Movability has nothing to do with this at all, except that the newly-added syntaxes that make move semantics convenient (i.e. rvalue references and move-constructor overload rules) mean that a "move constructor" can only be invoked when provided an rvalue expression. Of course, with the help of `std::move`, such an expression may still refer to _any_ movable object, even one with a name! – Lightness Races in Orbit Dec 27 '16 at 19:27
  • 1
    @LightnessRacesinOrbit, the `prvalue` comes under `rvalue` which BTW comes with move semantics when i read the standard it says`A prvalue (“pure” rvalue) is an rvalue that is not an xvalue` ain't sure "Movability has nothing to do with this at all" – RaGa__M Dec 27 '16 at 19:32
  • @FallingFromBed: I just explained the only way movability is linked to value category. Movability certainly doesn't dictate value category. _rvalues_ don't "come with move semantics". I'm not really sure what we're talking about any more. What is your question? – Lightness Races in Orbit Dec 27 '16 at 19:35
  • @LightnessRacesinOrbit "rvalues don't "come with move semantics" I really appreciate if you correct me saying what rvalue have to do with? – RaGa__M Dec 27 '16 at 19:50
  • @FallingFromBed: Perhaps you can tell me which of the above statements you disagreed with. That'll save me from fruitlessly repeating them. – Lightness Races in Orbit Dec 27 '16 at 20:24
1

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

Daniel Russell
  • 578
  • 4
  • 9
  • 1
    *"type of a C string literal is ... `char []`."* It's `const char [N]` in C++, and `char [N]` in C. The question is tagged C++. (Nitpicking: `const char []` is also a valid (but different) type.) *"`char[] my_string = ..."`* It should be `char my_string[] = ...`. *"String literals [are not] fundamental types"* All array types are non-fundamental. *"rvalues are not just fundamental types ... they are also temporaries"* Rvalues don't have to have fundamental types. They also don't necessarily refer to temporary objects. – HolyBlackCat Sep 09 '21 at 20:41
  • Thanks! Edited. Let me know if there's other things I can improve. I appreciate it! – Daniel Russell Sep 09 '21 at 21:03