Why are literals and temporary variables not lvalues?
I've read that lvalues are "things with a defined storage location".
And also that literals and temporaries variables are not lvalues, but no reason is given for this statement.
Is it because literals and temporary variables do not have defined storage location? If yes, then where do they reside if not in memory?
I suppose there is some significance to "defined" in "defined storage location", if there is (or is not) please let me know.
c++ c++11 rvalue lvalue
|
show 10 more comments
I've read that lvalues are "things with a defined storage location".
And also that literals and temporaries variables are not lvalues, but no reason is given for this statement.
Is it because literals and temporary variables do not have defined storage location? If yes, then where do they reside if not in memory?
I suppose there is some significance to "defined" in "defined storage location", if there is (or is not) please let me know.
c++ c++11 rvalue lvalue
1
It would be helpful if there is some good reference explaining lvalue and rvalues
– pasha
Feb 12 at 15:20
7
I find an easier rule of thumb is that lvalues can be named and their address can be taken. Maybe that's what they meant by "things with a defined storage location". They have an address that can be obtained.
– François Andrieux
Feb 12 at 15:20
1
@pasha Value categories.
– François Andrieux
Feb 12 at 15:21
1
temporaries do not need a defined storage location by definition. Considera = foo();there is no need to store the result offoo()anywhere but ina
– user463035818
Feb 12 at 15:21
17
"I've read that lvalues are 'things with a defined storage location.'" That's an oversimplification, and the problem with oversimplifications is that they are, sometimes, wrong. You should find the proper definition instead.
– Lightness Races in Orbit
Feb 12 at 15:21
|
show 10 more comments
I've read that lvalues are "things with a defined storage location".
And also that literals and temporaries variables are not lvalues, but no reason is given for this statement.
Is it because literals and temporary variables do not have defined storage location? If yes, then where do they reside if not in memory?
I suppose there is some significance to "defined" in "defined storage location", if there is (or is not) please let me know.
c++ c++11 rvalue lvalue
I've read that lvalues are "things with a defined storage location".
And also that literals and temporaries variables are not lvalues, but no reason is given for this statement.
Is it because literals and temporary variables do not have defined storage location? If yes, then where do they reside if not in memory?
I suppose there is some significance to "defined" in "defined storage location", if there is (or is not) please let me know.
c++ c++11 rvalue lvalue
c++ c++11 rvalue lvalue
edited Feb 12 at 22:32
Francesco Boi
2,61522543
2,61522543
asked Feb 12 at 15:17
pashapasha
920926
920926
1
It would be helpful if there is some good reference explaining lvalue and rvalues
– pasha
Feb 12 at 15:20
7
I find an easier rule of thumb is that lvalues can be named and their address can be taken. Maybe that's what they meant by "things with a defined storage location". They have an address that can be obtained.
– François Andrieux
Feb 12 at 15:20
1
@pasha Value categories.
– François Andrieux
Feb 12 at 15:21
1
temporaries do not need a defined storage location by definition. Considera = foo();there is no need to store the result offoo()anywhere but ina
– user463035818
Feb 12 at 15:21
17
"I've read that lvalues are 'things with a defined storage location.'" That's an oversimplification, and the problem with oversimplifications is that they are, sometimes, wrong. You should find the proper definition instead.
– Lightness Races in Orbit
Feb 12 at 15:21
|
show 10 more comments
1
It would be helpful if there is some good reference explaining lvalue and rvalues
– pasha
Feb 12 at 15:20
7
I find an easier rule of thumb is that lvalues can be named and their address can be taken. Maybe that's what they meant by "things with a defined storage location". They have an address that can be obtained.
– François Andrieux
Feb 12 at 15:20
1
@pasha Value categories.
– François Andrieux
Feb 12 at 15:21
1
temporaries do not need a defined storage location by definition. Considera = foo();there is no need to store the result offoo()anywhere but ina
– user463035818
Feb 12 at 15:21
17
"I've read that lvalues are 'things with a defined storage location.'" That's an oversimplification, and the problem with oversimplifications is that they are, sometimes, wrong. You should find the proper definition instead.
– Lightness Races in Orbit
Feb 12 at 15:21
1
1
It would be helpful if there is some good reference explaining lvalue and rvalues
– pasha
Feb 12 at 15:20
It would be helpful if there is some good reference explaining lvalue and rvalues
– pasha
Feb 12 at 15:20
7
7
I find an easier rule of thumb is that lvalues can be named and their address can be taken. Maybe that's what they meant by "things with a defined storage location". They have an address that can be obtained.
– François Andrieux
Feb 12 at 15:20
I find an easier rule of thumb is that lvalues can be named and their address can be taken. Maybe that's what they meant by "things with a defined storage location". They have an address that can be obtained.
– François Andrieux
Feb 12 at 15:20
1
1
@pasha Value categories.
– François Andrieux
Feb 12 at 15:21
@pasha Value categories.
– François Andrieux
Feb 12 at 15:21
1
1
temporaries do not need a defined storage location by definition. Consider
a = foo(); there is no need to store the result of foo() anywhere but in a– user463035818
Feb 12 at 15:21
temporaries do not need a defined storage location by definition. Consider
a = foo(); there is no need to store the result of foo() anywhere but in a– user463035818
Feb 12 at 15:21
17
17
"I've read that lvalues are 'things with a defined storage location.'" That's an oversimplification, and the problem with oversimplifications is that they are, sometimes, wrong. You should find the proper definition instead.
– Lightness Races in Orbit
Feb 12 at 15:21
"I've read that lvalues are 'things with a defined storage location.'" That's an oversimplification, and the problem with oversimplifications is that they are, sometimes, wrong. You should find the proper definition instead.
– Lightness Races in Orbit
Feb 12 at 15:21
|
show 10 more comments
5 Answers
5
active
oldest
votes
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.
why do string literals have storage but not other? Is it because they can't be embedded in single assembly instruction?
– pasha
Feb 12 at 16:15
10
@pasha Because C. In C, you can do nothing with a string literal other than working with its address. It has to be in memory. It has to have an address. So C++ adapted. This is really an edge case.
– YSC
Feb 12 at 16:16
3
@pasha Unlike other literals strings don't have a single value. Each character is a value and since you need to combine of them together they are stored in an array. Because it is an array it is no longer an rvalue because it is an object.
– NathanOliver
Feb 12 at 16:32
add a comment |
Why are literals and temporary variables not lvalues?
I have two answers: because it wouldn't make sense (1) and because the Standard says so (2). Let's focus on (1).
Is it because literals and temporaries variables do not have defined storage location?
This is a simplification that doesn't fit here. A simplification that would: literals and temporary are not lvalues because it wouldn't make sense to modify them1.
What is the meaning of 5++? What is the meaning of rand() = 0? The Standard says that temporaries and literals are not lvalues so those examples are invalid. And every compiler developer is happier.
1) You can define and use user-defined types in a way where the modification of a temporary makes sense. This temporary would live until the evaluation of the full-expression. François Andrieux makes a nice analogy between calling f(MyType{}.mutate()) on one hand and f(my_int + 1) on the other. I think the simplification holds still as MyType{}.mutate() can be seen as another temporary as MyType{} was, like my_int + 1 can be seen as another int as my_int was. This is all semantics and opinion-based. The real answer is: (2) because the Standard says so.
1
Your reasoning is a little flawed. Yes, modifying 5 is nonsense but you are allowed to modify temporaries of non built in types.
– NathanOliver
Feb 12 at 15:37
1
Temporary{}.Mutate()is valid though...
– Jarod42
Feb 12 at 15:37
@NathanOliver Yes, so side effects can happen. I think the reasoning (a simplification, let us not forget) holds still: except for specific cases (user-defined types with side effects on modification, string literals, ...) it means nothing to modify a pr-value.
– YSC
Feb 12 at 15:43
2
In C, which yes is a different language, you can indeed write(int){ 5 }++and that means something; yes there is a particular logic behind that, but it's a possible example of how the two reasons are interlinked - a literal not making sense as an lvalue is essentially down to design decisions made for the Standard - if the Standard wanted all values to be lvalues, they would be and it would make sense in that language as-designed.
– Leushenko
Feb 12 at 20:20
Another reason this reasoning is flawed is that a string literal is an lvalue, but "a" = 0 doesn't make sense.
– prl
Feb 13 at 4:42
|
show 1 more comment
There are a lot of common misconceptions in the question and in the other answers; my answer hopes to address that.
The terms lvalue and rvalue are expression categories. They are terms that apply to expressions. Not to objects. (A bit confusingly, the official term for expression categories is "value categories" ! )
The term temporary object refers to objects. This includes objects of class type, as well as objects of built-in type. The term temporary (used as a noun) is short for temporary object. Sometimes the standalone term value is used to refer to a temporary object of built-in type. These terms apply to objects, not to expressions.
The C++17 standard is more consistent in object terminology than past standards, e.g. see [conv.rval]/1. It now tries to avoid saying value other than in the context value of an expression.
Now, why are there different expression categories? A C++ program is made up of a collection of expressions, joined to each other with operators to make larger expressions; and fitting within a framework of declarative constructs. These expressions create, destroy, and do other manipulations on objects. Programming in C++ could be described as using expressions to perform operations with objects.
The reason that expression categories exist is to provide a framework for using expressions to express operations that the programmer intends. For example way back in the C days (and probably earlier), the language designers figured that 3 = 5; did not make any sense as part of a program so it was decided to limit what sort of expression can appear on the left-hand side of =, and have the compiler report an error if this restriction wasn't followed.
The term lvalue originated in those days, although now with the development of C++ there are a vast range of expressions and contexts where expression categories are useful, not just the left-hand side of an assignment operator.
Here is some valid C++ code: std::string("3") = std::string("5");. This is conceptually no different from 3 = 5;, however it is allowed. The effect is that a temporary object of type std::string and content "3" is created, and then that temporary object is modified to have content "5", and then the temporary object is destroyed. The language could have been designed so that the code 3 = 5; specifies a similar series of events (but it wasn't).
Why is the string example legal but the int example not?
Every expression has to have a category. The category of an expression might not seem to have an obvious reason at first, but the designers of the language have given each expression a category according to what they think is a useful concept to express and what isn't.
It's been decided that the sequence of events in 3 = 5; as described above is not something anyone would want to do, and if someone did write such a thing then they probably made a mistake and meant something else, so the compiler should help out by giving an error message.
Now, the same logic might conclude that std::string("3") = std::string("5") is not something anyone would ever want to do either. However another argument is that for some other class type, T(foo) = x; might actually be a worthwhile operation, e.g. because T might have a destructor that does something. It was decided that banning this usage could be more harmful to a programmer's intentions than good. (Whether that was a good decision or not is debatable; see this question for discussion).
Now we are getting closer to finally address your question :)
Whether or not there is memory or a storage location associated is not the rationale for expression categories any more. In the abstract machine (more explanation of this below), every temporary object (this includes the one created by 3 in x = 3;) exists in memory.
As described earlier in my answer, a program consists of expressions that manipulate objects. Each expression is said to designate or refer to an object.
It's very common for other answers or articles on this topic to make the incorrect claim that an rvalue can only designate a temporary object, or even worse , that an rvalue is a temporary object , or that a temporary object is an rvalue. An expression is not an object, it is something that occurs in source code for manipulating objects!
In fact a temporary object can be designated by an lvalue or an rvalue expression; and a non-temporary object can be designated by an lvalue or an rvalue expression. They are separate concepts.
Now, there's an expression category rule that you can't apply & to an expression of the rvalue category. The purpose of this rule and these categories is to avoid errors where a temporary object is used after it is destroyed. For example:
int *p = &5; // not allowed due to category rules
*p = 6; // oops, dangling pointer
But you could get around this:
template<typename T> auto f(T&&t) -> T& { return t; }
// ...
int *p = f(5); // Allowed
*p = 6; // Oops, dangling pointer, no compiler error message.
In this latter code, f(5) and *p are both lvalues that designate a temporary object. This is a good example of why the expression category rules exist; by following the rules without a tricky workaround, then we would get an error for the code that tries to write through a dangling pointer.
Note that you can also use this f to find the memory address of a temporary object, e.g. std::cout << &f(5);
In summary, the questions you actually ask all mistakenly conflate expressions with objects. So they are non-questions in that sense. Temporaries are not lvalues, because objects are not expressions.
A valid but related question would be: "Why is the expression that creates a temporary object an rvalue (as opposed to being an lvalue?)"
To which the answer is as was discussed above: having it be an lvalue would increase the risk of creating dangling pointers or dangling references; and as in 3 = 5;, would increase the risk of specifying redundant operations that the programmer probably didn't intend.
I repeat again that the expression categories are a design decision to help with programmer expressiveness; not anything to do with memory or storage locations.
Finally, to the abstract machine and the as-if rule. C++ is defined in terms of an abstract machine, in which temporary objects have storage and addresses too. I gave an example earlier of how to print the address of a temporary object.
The as-if rule says that the output of the actual executable the compiler produces must only match the output that the abstract machine would. The executable doesn't actually have to work in the same way as the abstract machine, it just has to produce the same result.
So for code like x = 5; , even though a temporary object of value 5 has a memory location in the abstract machine; the compiler doesn't have to allocate physical storage on the real machine. It only has to ensure that x ends up having 5 stored in it and there are much easier ways to do this that don't involve extra storage being created.
The as-if rule applies to everything in the program, even though my example here only refers to temporary objects. A non-temporary object could equally well be optimized out, e.g. int x; int y = 5; x = y; // other code that doesn't use y could be changed to int x = 5;.
The same applies for class types without side-effects that would alter the program output. E.g. std::string x = "foo"; std::cout << x; can be optimized to std::cout << "foo"; even though the lvalue x denoted an object with storage in the abstract machine.
+1 I always assumed the l and r in lvalue/rvalue stood for left and right in the context of an assignment. Is this correct? If so then I would write this very conspicuously in the answer since it's pretty much a very short and to-the-point answer to the question.
– Mehrdad
Feb 13 at 3:17
@Mehrdad, that is where the names come from, and that is what they originally meant, but then X3J11 introduced the notion of a modifiable lvalue and things got murkier.
– prl
Feb 13 at 4:38
@Mehrdad my second section addresses that (and I don't feel your suggestion would be a correct answer, let alone a complete one)
– M.M
Feb 13 at 5:32
Another example of why it makes no sense to allow taking the address of a value:assert(&5 == &5);– assuming that&5was a legal expression, would or should this hold? Would it even be useful in some way to ask whether&5 == &5? I for one cannot possibly imagine what the point would be.
– Arne Vogel
Feb 13 at 14:55
Nice answer (+1) but very easy to get lost (maybe it's just me) so I have the following observations 1)lvalues andrvalues are expression categories, not strictly relate to temporary or non-temporary object andIn fact a temporary object can be designated by an lvalue or an rvalue expression; and a non-temporary object can be designated by an lvalue or an rvalue expression.It is clear from your answer whatlvaluesandrvaluesare not, but could you point out (more clearly maybe) what they are then? 2) Can you explain the syntax-> T& { return t; }?
– Francesco Boi
Feb 18 at 15:28
|
show 5 more comments
lvalue stands for locator value and represents an object that occupies some identifiable location in memory.
The term locator value is also used here:
C
The C programming language followed a similar taxonomy, except that
the role of assignment was no longer significant: C expressions are
categorized between "lvalue expressions" and others (functions and
non-object values), where "lvalue" means an expression that identifies
an object, a "locator value"[4].
Everything that is not an lvalue is by exclusion an rvalue. Every expression is either an lavalue or rvalue.
Originally lvalue term was used in C to indicate values that can stay on the left side of assignment operator. However with the const keywork this changed. Not all lvalues can be assigned to. Those that can are called modifiable lvalues.
And also that literals and temporaries variables are not lvalues, but
no reason is given for this statement.
According to this answer literals can be lvalues in some cases.
literals of scalar types arervaluebecause they are of known size and are very likely to be embedded directly into the machine commands on the given hardware architecture. What would be the memory location of5?- On the contrary, strangely enough, string literals are
lvaluessince they have unpredictable size and there is no other way to represent them apart from as objects in memory.
An lvalue can be converted to an rvalue. For example in the following instructions
int a =5;
int b = 3;
int c = a+b;
the operator + takes two rvalues. So a and b are converted to rvalues before getting summed. Another example of conversion:
int c = 6;
&c = 4; //ERROR: &c is an rvalue
On the contrary you cannot convert an rvalue to an lvalue.
However you can produce a valid lvalue from an rvalue for example:
int arr = {1, 2};
int* p = &arr[0];
*(p + 1) = 10; // OK: p + 1 is an rvalue, but *(p + 1) is an lvalue
In C++11 rvalues reference are related to the move constructor and move assignment operator.
You can find more details in this clear and well-explained post.
1
I added another link in the answer check theCpaaragraph: en.cppreference.com/w/cpp/language/value_category
– Francesco Boi
Feb 12 at 16:29
1
"Hello worldn"has a definite address.
– NathanOliver
Feb 12 at 16:34
2
It's&"Hello worldn"see: coliru.stacked-crooked.com/a/c19f1a7fa216cdc5
– NathanOliver
Feb 12 at 16:36
"On the contrary you cannot convert an rvalue to an lvalue" - yes you can: writetemplate<typename T> auto f(T&& t) -> T& { return t; }, then callingfon an rvalue will yield an lvalue referring to the same object. E.g.cout << &f(5);
– M.M
Feb 12 at 23:05
@M.M Honestly those lines are not really clear to me so I cannot modify my answer to explain them. If you have some reference I will gladly read it and try to modify my answer accordingly. Also feel free to modify my answer yourself if worth it.
– Francesco Boi
Feb 13 at 8:40
|
show 8 more comments
Where do they reside if not in memory?
Of course they reside in memory*, there's no way around it. The question is, can your program determine where exactly in memory do they reside. In other words, is your program allowed to take the address of the thing in question.
In a simple example a = 5 the value of five, or an instruction representing an assignment of the value of five, is somewhere in memory. However, you cannot take the address of five, because int *p = &5 is illegal.
Note that string literals are an exception from the "not an lvalue" rule, because const char *p = "hello" produces an address of a string literal.
* However, it may not necessarily be data memory. In fact, they may not even be represented as a constant in the program memory: for example, an assignment
short a; a = 0xFF00 could be represented as a an assignment of 0xFF in the upper octet, and clearing out the lower octet in memory.
You can take address of anlvalue:&++ior&std::endl
– Dan M.
Feb 12 at 15:40
1
"you cannot take the address of five".const int& v = 5;is legal though.
– Jarod42
Feb 12 at 15:40
8
@Jarod42 That is not taking the address of5though. That materialized a temporaryintobject with the value of5and binds the reference to that object, not to the5.
– NathanOliver
Feb 12 at 15:46
5
OrXOR AX,AX; DEC AH... or lots of other strategies.
– Martin Bonner
Feb 12 at 16:17
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54653276%2fwhy-are-literals-and-temporary-variables-not-lvalues%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
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.
why do string literals have storage but not other? Is it because they can't be embedded in single assembly instruction?
– pasha
Feb 12 at 16:15
10
@pasha Because C. In C, you can do nothing with a string literal other than working with its address. It has to be in memory. It has to have an address. So C++ adapted. This is really an edge case.
– YSC
Feb 12 at 16:16
3
@pasha Unlike other literals strings don't have a single value. Each character is a value and since you need to combine of them together they are stored in an array. Because it is an array it is no longer an rvalue because it is an object.
– NathanOliver
Feb 12 at 16:32
add a comment |
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.
why do string literals have storage but not other? Is it because they can't be embedded in single assembly instruction?
– pasha
Feb 12 at 16:15
10
@pasha Because C. In C, you can do nothing with a string literal other than working with its address. It has to be in memory. It has to have an address. So C++ adapted. This is really an edge case.
– YSC
Feb 12 at 16:16
3
@pasha Unlike other literals strings don't have a single value. Each character is a value and since you need to combine of them together they are stored in an array. Because it is an array it is no longer an rvalue because it is an object.
– NathanOliver
Feb 12 at 16:32
add a comment |
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.
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.
edited Feb 15 at 11:33
insert_name_here
43749
43749
answered Feb 12 at 15:32
NathanOliverNathanOliver
95.1k16132203
95.1k16132203
why do string literals have storage but not other? Is it because they can't be embedded in single assembly instruction?
– pasha
Feb 12 at 16:15
10
@pasha Because C. In C, you can do nothing with a string literal other than working with its address. It has to be in memory. It has to have an address. So C++ adapted. This is really an edge case.
– YSC
Feb 12 at 16:16
3
@pasha Unlike other literals strings don't have a single value. Each character is a value and since you need to combine of them together they are stored in an array. Because it is an array it is no longer an rvalue because it is an object.
– NathanOliver
Feb 12 at 16:32
add a comment |
why do string literals have storage but not other? Is it because they can't be embedded in single assembly instruction?
– pasha
Feb 12 at 16:15
10
@pasha Because C. In C, you can do nothing with a string literal other than working with its address. It has to be in memory. It has to have an address. So C++ adapted. This is really an edge case.
– YSC
Feb 12 at 16:16
3
@pasha Unlike other literals strings don't have a single value. Each character is a value and since you need to combine of them together they are stored in an array. Because it is an array it is no longer an rvalue because it is an object.
– NathanOliver
Feb 12 at 16:32
why do string literals have storage but not other? Is it because they can't be embedded in single assembly instruction?
– pasha
Feb 12 at 16:15
why do string literals have storage but not other? Is it because they can't be embedded in single assembly instruction?
– pasha
Feb 12 at 16:15
10
10
@pasha Because C. In C, you can do nothing with a string literal other than working with its address. It has to be in memory. It has to have an address. So C++ adapted. This is really an edge case.
– YSC
Feb 12 at 16:16
@pasha Because C. In C, you can do nothing with a string literal other than working with its address. It has to be in memory. It has to have an address. So C++ adapted. This is really an edge case.
– YSC
Feb 12 at 16:16
3
3
@pasha Unlike other literals strings don't have a single value. Each character is a value and since you need to combine of them together they are stored in an array. Because it is an array it is no longer an rvalue because it is an object.
– NathanOliver
Feb 12 at 16:32
@pasha Unlike other literals strings don't have a single value. Each character is a value and since you need to combine of them together they are stored in an array. Because it is an array it is no longer an rvalue because it is an object.
– NathanOliver
Feb 12 at 16:32
add a comment |
Why are literals and temporary variables not lvalues?
I have two answers: because it wouldn't make sense (1) and because the Standard says so (2). Let's focus on (1).
Is it because literals and temporaries variables do not have defined storage location?
This is a simplification that doesn't fit here. A simplification that would: literals and temporary are not lvalues because it wouldn't make sense to modify them1.
What is the meaning of 5++? What is the meaning of rand() = 0? The Standard says that temporaries and literals are not lvalues so those examples are invalid. And every compiler developer is happier.
1) You can define and use user-defined types in a way where the modification of a temporary makes sense. This temporary would live until the evaluation of the full-expression. François Andrieux makes a nice analogy between calling f(MyType{}.mutate()) on one hand and f(my_int + 1) on the other. I think the simplification holds still as MyType{}.mutate() can be seen as another temporary as MyType{} was, like my_int + 1 can be seen as another int as my_int was. This is all semantics and opinion-based. The real answer is: (2) because the Standard says so.
1
Your reasoning is a little flawed. Yes, modifying 5 is nonsense but you are allowed to modify temporaries of non built in types.
– NathanOliver
Feb 12 at 15:37
1
Temporary{}.Mutate()is valid though...
– Jarod42
Feb 12 at 15:37
@NathanOliver Yes, so side effects can happen. I think the reasoning (a simplification, let us not forget) holds still: except for specific cases (user-defined types with side effects on modification, string literals, ...) it means nothing to modify a pr-value.
– YSC
Feb 12 at 15:43
2
In C, which yes is a different language, you can indeed write(int){ 5 }++and that means something; yes there is a particular logic behind that, but it's a possible example of how the two reasons are interlinked - a literal not making sense as an lvalue is essentially down to design decisions made for the Standard - if the Standard wanted all values to be lvalues, they would be and it would make sense in that language as-designed.
– Leushenko
Feb 12 at 20:20
Another reason this reasoning is flawed is that a string literal is an lvalue, but "a" = 0 doesn't make sense.
– prl
Feb 13 at 4:42
|
show 1 more comment
Why are literals and temporary variables not lvalues?
I have two answers: because it wouldn't make sense (1) and because the Standard says so (2). Let's focus on (1).
Is it because literals and temporaries variables do not have defined storage location?
This is a simplification that doesn't fit here. A simplification that would: literals and temporary are not lvalues because it wouldn't make sense to modify them1.
What is the meaning of 5++? What is the meaning of rand() = 0? The Standard says that temporaries and literals are not lvalues so those examples are invalid. And every compiler developer is happier.
1) You can define and use user-defined types in a way where the modification of a temporary makes sense. This temporary would live until the evaluation of the full-expression. François Andrieux makes a nice analogy between calling f(MyType{}.mutate()) on one hand and f(my_int + 1) on the other. I think the simplification holds still as MyType{}.mutate() can be seen as another temporary as MyType{} was, like my_int + 1 can be seen as another int as my_int was. This is all semantics and opinion-based. The real answer is: (2) because the Standard says so.
1
Your reasoning is a little flawed. Yes, modifying 5 is nonsense but you are allowed to modify temporaries of non built in types.
– NathanOliver
Feb 12 at 15:37
1
Temporary{}.Mutate()is valid though...
– Jarod42
Feb 12 at 15:37
@NathanOliver Yes, so side effects can happen. I think the reasoning (a simplification, let us not forget) holds still: except for specific cases (user-defined types with side effects on modification, string literals, ...) it means nothing to modify a pr-value.
– YSC
Feb 12 at 15:43
2
In C, which yes is a different language, you can indeed write(int){ 5 }++and that means something; yes there is a particular logic behind that, but it's a possible example of how the two reasons are interlinked - a literal not making sense as an lvalue is essentially down to design decisions made for the Standard - if the Standard wanted all values to be lvalues, they would be and it would make sense in that language as-designed.
– Leushenko
Feb 12 at 20:20
Another reason this reasoning is flawed is that a string literal is an lvalue, but "a" = 0 doesn't make sense.
– prl
Feb 13 at 4:42
|
show 1 more comment
Why are literals and temporary variables not lvalues?
I have two answers: because it wouldn't make sense (1) and because the Standard says so (2). Let's focus on (1).
Is it because literals and temporaries variables do not have defined storage location?
This is a simplification that doesn't fit here. A simplification that would: literals and temporary are not lvalues because it wouldn't make sense to modify them1.
What is the meaning of 5++? What is the meaning of rand() = 0? The Standard says that temporaries and literals are not lvalues so those examples are invalid. And every compiler developer is happier.
1) You can define and use user-defined types in a way where the modification of a temporary makes sense. This temporary would live until the evaluation of the full-expression. François Andrieux makes a nice analogy between calling f(MyType{}.mutate()) on one hand and f(my_int + 1) on the other. I think the simplification holds still as MyType{}.mutate() can be seen as another temporary as MyType{} was, like my_int + 1 can be seen as another int as my_int was. This is all semantics and opinion-based. The real answer is: (2) because the Standard says so.
Why are literals and temporary variables not lvalues?
I have two answers: because it wouldn't make sense (1) and because the Standard says so (2). Let's focus on (1).
Is it because literals and temporaries variables do not have defined storage location?
This is a simplification that doesn't fit here. A simplification that would: literals and temporary are not lvalues because it wouldn't make sense to modify them1.
What is the meaning of 5++? What is the meaning of rand() = 0? The Standard says that temporaries and literals are not lvalues so those examples are invalid. And every compiler developer is happier.
1) You can define and use user-defined types in a way where the modification of a temporary makes sense. This temporary would live until the evaluation of the full-expression. François Andrieux makes a nice analogy between calling f(MyType{}.mutate()) on one hand and f(my_int + 1) on the other. I think the simplification holds still as MyType{}.mutate() can be seen as another temporary as MyType{} was, like my_int + 1 can be seen as another int as my_int was. This is all semantics and opinion-based. The real answer is: (2) because the Standard says so.
edited Feb 12 at 16:22
answered Feb 12 at 15:33
YSCYSC
25.2k557112
25.2k557112
1
Your reasoning is a little flawed. Yes, modifying 5 is nonsense but you are allowed to modify temporaries of non built in types.
– NathanOliver
Feb 12 at 15:37
1
Temporary{}.Mutate()is valid though...
– Jarod42
Feb 12 at 15:37
@NathanOliver Yes, so side effects can happen. I think the reasoning (a simplification, let us not forget) holds still: except for specific cases (user-defined types with side effects on modification, string literals, ...) it means nothing to modify a pr-value.
– YSC
Feb 12 at 15:43
2
In C, which yes is a different language, you can indeed write(int){ 5 }++and that means something; yes there is a particular logic behind that, but it's a possible example of how the two reasons are interlinked - a literal not making sense as an lvalue is essentially down to design decisions made for the Standard - if the Standard wanted all values to be lvalues, they would be and it would make sense in that language as-designed.
– Leushenko
Feb 12 at 20:20
Another reason this reasoning is flawed is that a string literal is an lvalue, but "a" = 0 doesn't make sense.
– prl
Feb 13 at 4:42
|
show 1 more comment
1
Your reasoning is a little flawed. Yes, modifying 5 is nonsense but you are allowed to modify temporaries of non built in types.
– NathanOliver
Feb 12 at 15:37
1
Temporary{}.Mutate()is valid though...
– Jarod42
Feb 12 at 15:37
@NathanOliver Yes, so side effects can happen. I think the reasoning (a simplification, let us not forget) holds still: except for specific cases (user-defined types with side effects on modification, string literals, ...) it means nothing to modify a pr-value.
– YSC
Feb 12 at 15:43
2
In C, which yes is a different language, you can indeed write(int){ 5 }++and that means something; yes there is a particular logic behind that, but it's a possible example of how the two reasons are interlinked - a literal not making sense as an lvalue is essentially down to design decisions made for the Standard - if the Standard wanted all values to be lvalues, they would be and it would make sense in that language as-designed.
– Leushenko
Feb 12 at 20:20
Another reason this reasoning is flawed is that a string literal is an lvalue, but "a" = 0 doesn't make sense.
– prl
Feb 13 at 4:42
1
1
Your reasoning is a little flawed. Yes, modifying 5 is nonsense but you are allowed to modify temporaries of non built in types.
– NathanOliver
Feb 12 at 15:37
Your reasoning is a little flawed. Yes, modifying 5 is nonsense but you are allowed to modify temporaries of non built in types.
– NathanOliver
Feb 12 at 15:37
1
1
Temporary{}.Mutate() is valid though...– Jarod42
Feb 12 at 15:37
Temporary{}.Mutate() is valid though...– Jarod42
Feb 12 at 15:37
@NathanOliver Yes, so side effects can happen. I think the reasoning (a simplification, let us not forget) holds still: except for specific cases (user-defined types with side effects on modification, string literals, ...) it means nothing to modify a pr-value.
– YSC
Feb 12 at 15:43
@NathanOliver Yes, so side effects can happen. I think the reasoning (a simplification, let us not forget) holds still: except for specific cases (user-defined types with side effects on modification, string literals, ...) it means nothing to modify a pr-value.
– YSC
Feb 12 at 15:43
2
2
In C, which yes is a different language, you can indeed write
(int){ 5 }++ and that means something; yes there is a particular logic behind that, but it's a possible example of how the two reasons are interlinked - a literal not making sense as an lvalue is essentially down to design decisions made for the Standard - if the Standard wanted all values to be lvalues, they would be and it would make sense in that language as-designed.– Leushenko
Feb 12 at 20:20
In C, which yes is a different language, you can indeed write
(int){ 5 }++ and that means something; yes there is a particular logic behind that, but it's a possible example of how the two reasons are interlinked - a literal not making sense as an lvalue is essentially down to design decisions made for the Standard - if the Standard wanted all values to be lvalues, they would be and it would make sense in that language as-designed.– Leushenko
Feb 12 at 20:20
Another reason this reasoning is flawed is that a string literal is an lvalue, but "a" = 0 doesn't make sense.
– prl
Feb 13 at 4:42
Another reason this reasoning is flawed is that a string literal is an lvalue, but "a" = 0 doesn't make sense.
– prl
Feb 13 at 4:42
|
show 1 more comment
There are a lot of common misconceptions in the question and in the other answers; my answer hopes to address that.
The terms lvalue and rvalue are expression categories. They are terms that apply to expressions. Not to objects. (A bit confusingly, the official term for expression categories is "value categories" ! )
The term temporary object refers to objects. This includes objects of class type, as well as objects of built-in type. The term temporary (used as a noun) is short for temporary object. Sometimes the standalone term value is used to refer to a temporary object of built-in type. These terms apply to objects, not to expressions.
The C++17 standard is more consistent in object terminology than past standards, e.g. see [conv.rval]/1. It now tries to avoid saying value other than in the context value of an expression.
Now, why are there different expression categories? A C++ program is made up of a collection of expressions, joined to each other with operators to make larger expressions; and fitting within a framework of declarative constructs. These expressions create, destroy, and do other manipulations on objects. Programming in C++ could be described as using expressions to perform operations with objects.
The reason that expression categories exist is to provide a framework for using expressions to express operations that the programmer intends. For example way back in the C days (and probably earlier), the language designers figured that 3 = 5; did not make any sense as part of a program so it was decided to limit what sort of expression can appear on the left-hand side of =, and have the compiler report an error if this restriction wasn't followed.
The term lvalue originated in those days, although now with the development of C++ there are a vast range of expressions and contexts where expression categories are useful, not just the left-hand side of an assignment operator.
Here is some valid C++ code: std::string("3") = std::string("5");. This is conceptually no different from 3 = 5;, however it is allowed. The effect is that a temporary object of type std::string and content "3" is created, and then that temporary object is modified to have content "5", and then the temporary object is destroyed. The language could have been designed so that the code 3 = 5; specifies a similar series of events (but it wasn't).
Why is the string example legal but the int example not?
Every expression has to have a category. The category of an expression might not seem to have an obvious reason at first, but the designers of the language have given each expression a category according to what they think is a useful concept to express and what isn't.
It's been decided that the sequence of events in 3 = 5; as described above is not something anyone would want to do, and if someone did write such a thing then they probably made a mistake and meant something else, so the compiler should help out by giving an error message.
Now, the same logic might conclude that std::string("3") = std::string("5") is not something anyone would ever want to do either. However another argument is that for some other class type, T(foo) = x; might actually be a worthwhile operation, e.g. because T might have a destructor that does something. It was decided that banning this usage could be more harmful to a programmer's intentions than good. (Whether that was a good decision or not is debatable; see this question for discussion).
Now we are getting closer to finally address your question :)
Whether or not there is memory or a storage location associated is not the rationale for expression categories any more. In the abstract machine (more explanation of this below), every temporary object (this includes the one created by 3 in x = 3;) exists in memory.
As described earlier in my answer, a program consists of expressions that manipulate objects. Each expression is said to designate or refer to an object.
It's very common for other answers or articles on this topic to make the incorrect claim that an rvalue can only designate a temporary object, or even worse , that an rvalue is a temporary object , or that a temporary object is an rvalue. An expression is not an object, it is something that occurs in source code for manipulating objects!
In fact a temporary object can be designated by an lvalue or an rvalue expression; and a non-temporary object can be designated by an lvalue or an rvalue expression. They are separate concepts.
Now, there's an expression category rule that you can't apply & to an expression of the rvalue category. The purpose of this rule and these categories is to avoid errors where a temporary object is used after it is destroyed. For example:
int *p = &5; // not allowed due to category rules
*p = 6; // oops, dangling pointer
But you could get around this:
template<typename T> auto f(T&&t) -> T& { return t; }
// ...
int *p = f(5); // Allowed
*p = 6; // Oops, dangling pointer, no compiler error message.
In this latter code, f(5) and *p are both lvalues that designate a temporary object. This is a good example of why the expression category rules exist; by following the rules without a tricky workaround, then we would get an error for the code that tries to write through a dangling pointer.
Note that you can also use this f to find the memory address of a temporary object, e.g. std::cout << &f(5);
In summary, the questions you actually ask all mistakenly conflate expressions with objects. So they are non-questions in that sense. Temporaries are not lvalues, because objects are not expressions.
A valid but related question would be: "Why is the expression that creates a temporary object an rvalue (as opposed to being an lvalue?)"
To which the answer is as was discussed above: having it be an lvalue would increase the risk of creating dangling pointers or dangling references; and as in 3 = 5;, would increase the risk of specifying redundant operations that the programmer probably didn't intend.
I repeat again that the expression categories are a design decision to help with programmer expressiveness; not anything to do with memory or storage locations.
Finally, to the abstract machine and the as-if rule. C++ is defined in terms of an abstract machine, in which temporary objects have storage and addresses too. I gave an example earlier of how to print the address of a temporary object.
The as-if rule says that the output of the actual executable the compiler produces must only match the output that the abstract machine would. The executable doesn't actually have to work in the same way as the abstract machine, it just has to produce the same result.
So for code like x = 5; , even though a temporary object of value 5 has a memory location in the abstract machine; the compiler doesn't have to allocate physical storage on the real machine. It only has to ensure that x ends up having 5 stored in it and there are much easier ways to do this that don't involve extra storage being created.
The as-if rule applies to everything in the program, even though my example here only refers to temporary objects. A non-temporary object could equally well be optimized out, e.g. int x; int y = 5; x = y; // other code that doesn't use y could be changed to int x = 5;.
The same applies for class types without side-effects that would alter the program output. E.g. std::string x = "foo"; std::cout << x; can be optimized to std::cout << "foo"; even though the lvalue x denoted an object with storage in the abstract machine.
+1 I always assumed the l and r in lvalue/rvalue stood for left and right in the context of an assignment. Is this correct? If so then I would write this very conspicuously in the answer since it's pretty much a very short and to-the-point answer to the question.
– Mehrdad
Feb 13 at 3:17
@Mehrdad, that is where the names come from, and that is what they originally meant, but then X3J11 introduced the notion of a modifiable lvalue and things got murkier.
– prl
Feb 13 at 4:38
@Mehrdad my second section addresses that (and I don't feel your suggestion would be a correct answer, let alone a complete one)
– M.M
Feb 13 at 5:32
Another example of why it makes no sense to allow taking the address of a value:assert(&5 == &5);– assuming that&5was a legal expression, would or should this hold? Would it even be useful in some way to ask whether&5 == &5? I for one cannot possibly imagine what the point would be.
– Arne Vogel
Feb 13 at 14:55
Nice answer (+1) but very easy to get lost (maybe it's just me) so I have the following observations 1)lvalues andrvalues are expression categories, not strictly relate to temporary or non-temporary object andIn fact a temporary object can be designated by an lvalue or an rvalue expression; and a non-temporary object can be designated by an lvalue or an rvalue expression.It is clear from your answer whatlvaluesandrvaluesare not, but could you point out (more clearly maybe) what they are then? 2) Can you explain the syntax-> T& { return t; }?
– Francesco Boi
Feb 18 at 15:28
|
show 5 more comments
There are a lot of common misconceptions in the question and in the other answers; my answer hopes to address that.
The terms lvalue and rvalue are expression categories. They are terms that apply to expressions. Not to objects. (A bit confusingly, the official term for expression categories is "value categories" ! )
The term temporary object refers to objects. This includes objects of class type, as well as objects of built-in type. The term temporary (used as a noun) is short for temporary object. Sometimes the standalone term value is used to refer to a temporary object of built-in type. These terms apply to objects, not to expressions.
The C++17 standard is more consistent in object terminology than past standards, e.g. see [conv.rval]/1. It now tries to avoid saying value other than in the context value of an expression.
Now, why are there different expression categories? A C++ program is made up of a collection of expressions, joined to each other with operators to make larger expressions; and fitting within a framework of declarative constructs. These expressions create, destroy, and do other manipulations on objects. Programming in C++ could be described as using expressions to perform operations with objects.
The reason that expression categories exist is to provide a framework for using expressions to express operations that the programmer intends. For example way back in the C days (and probably earlier), the language designers figured that 3 = 5; did not make any sense as part of a program so it was decided to limit what sort of expression can appear on the left-hand side of =, and have the compiler report an error if this restriction wasn't followed.
The term lvalue originated in those days, although now with the development of C++ there are a vast range of expressions and contexts where expression categories are useful, not just the left-hand side of an assignment operator.
Here is some valid C++ code: std::string("3") = std::string("5");. This is conceptually no different from 3 = 5;, however it is allowed. The effect is that a temporary object of type std::string and content "3" is created, and then that temporary object is modified to have content "5", and then the temporary object is destroyed. The language could have been designed so that the code 3 = 5; specifies a similar series of events (but it wasn't).
Why is the string example legal but the int example not?
Every expression has to have a category. The category of an expression might not seem to have an obvious reason at first, but the designers of the language have given each expression a category according to what they think is a useful concept to express and what isn't.
It's been decided that the sequence of events in 3 = 5; as described above is not something anyone would want to do, and if someone did write such a thing then they probably made a mistake and meant something else, so the compiler should help out by giving an error message.
Now, the same logic might conclude that std::string("3") = std::string("5") is not something anyone would ever want to do either. However another argument is that for some other class type, T(foo) = x; might actually be a worthwhile operation, e.g. because T might have a destructor that does something. It was decided that banning this usage could be more harmful to a programmer's intentions than good. (Whether that was a good decision or not is debatable; see this question for discussion).
Now we are getting closer to finally address your question :)
Whether or not there is memory or a storage location associated is not the rationale for expression categories any more. In the abstract machine (more explanation of this below), every temporary object (this includes the one created by 3 in x = 3;) exists in memory.
As described earlier in my answer, a program consists of expressions that manipulate objects. Each expression is said to designate or refer to an object.
It's very common for other answers or articles on this topic to make the incorrect claim that an rvalue can only designate a temporary object, or even worse , that an rvalue is a temporary object , or that a temporary object is an rvalue. An expression is not an object, it is something that occurs in source code for manipulating objects!
In fact a temporary object can be designated by an lvalue or an rvalue expression; and a non-temporary object can be designated by an lvalue or an rvalue expression. They are separate concepts.
Now, there's an expression category rule that you can't apply & to an expression of the rvalue category. The purpose of this rule and these categories is to avoid errors where a temporary object is used after it is destroyed. For example:
int *p = &5; // not allowed due to category rules
*p = 6; // oops, dangling pointer
But you could get around this:
template<typename T> auto f(T&&t) -> T& { return t; }
// ...
int *p = f(5); // Allowed
*p = 6; // Oops, dangling pointer, no compiler error message.
In this latter code, f(5) and *p are both lvalues that designate a temporary object. This is a good example of why the expression category rules exist; by following the rules without a tricky workaround, then we would get an error for the code that tries to write through a dangling pointer.
Note that you can also use this f to find the memory address of a temporary object, e.g. std::cout << &f(5);
In summary, the questions you actually ask all mistakenly conflate expressions with objects. So they are non-questions in that sense. Temporaries are not lvalues, because objects are not expressions.
A valid but related question would be: "Why is the expression that creates a temporary object an rvalue (as opposed to being an lvalue?)"
To which the answer is as was discussed above: having it be an lvalue would increase the risk of creating dangling pointers or dangling references; and as in 3 = 5;, would increase the risk of specifying redundant operations that the programmer probably didn't intend.
I repeat again that the expression categories are a design decision to help with programmer expressiveness; not anything to do with memory or storage locations.
Finally, to the abstract machine and the as-if rule. C++ is defined in terms of an abstract machine, in which temporary objects have storage and addresses too. I gave an example earlier of how to print the address of a temporary object.
The as-if rule says that the output of the actual executable the compiler produces must only match the output that the abstract machine would. The executable doesn't actually have to work in the same way as the abstract machine, it just has to produce the same result.
So for code like x = 5; , even though a temporary object of value 5 has a memory location in the abstract machine; the compiler doesn't have to allocate physical storage on the real machine. It only has to ensure that x ends up having 5 stored in it and there are much easier ways to do this that don't involve extra storage being created.
The as-if rule applies to everything in the program, even though my example here only refers to temporary objects. A non-temporary object could equally well be optimized out, e.g. int x; int y = 5; x = y; // other code that doesn't use y could be changed to int x = 5;.
The same applies for class types without side-effects that would alter the program output. E.g. std::string x = "foo"; std::cout << x; can be optimized to std::cout << "foo"; even though the lvalue x denoted an object with storage in the abstract machine.
+1 I always assumed the l and r in lvalue/rvalue stood for left and right in the context of an assignment. Is this correct? If so then I would write this very conspicuously in the answer since it's pretty much a very short and to-the-point answer to the question.
– Mehrdad
Feb 13 at 3:17
@Mehrdad, that is where the names come from, and that is what they originally meant, but then X3J11 introduced the notion of a modifiable lvalue and things got murkier.
– prl
Feb 13 at 4:38
@Mehrdad my second section addresses that (and I don't feel your suggestion would be a correct answer, let alone a complete one)
– M.M
Feb 13 at 5:32
Another example of why it makes no sense to allow taking the address of a value:assert(&5 == &5);– assuming that&5was a legal expression, would or should this hold? Would it even be useful in some way to ask whether&5 == &5? I for one cannot possibly imagine what the point would be.
– Arne Vogel
Feb 13 at 14:55
Nice answer (+1) but very easy to get lost (maybe it's just me) so I have the following observations 1)lvalues andrvalues are expression categories, not strictly relate to temporary or non-temporary object andIn fact a temporary object can be designated by an lvalue or an rvalue expression; and a non-temporary object can be designated by an lvalue or an rvalue expression.It is clear from your answer whatlvaluesandrvaluesare not, but could you point out (more clearly maybe) what they are then? 2) Can you explain the syntax-> T& { return t; }?
– Francesco Boi
Feb 18 at 15:28
|
show 5 more comments
There are a lot of common misconceptions in the question and in the other answers; my answer hopes to address that.
The terms lvalue and rvalue are expression categories. They are terms that apply to expressions. Not to objects. (A bit confusingly, the official term for expression categories is "value categories" ! )
The term temporary object refers to objects. This includes objects of class type, as well as objects of built-in type. The term temporary (used as a noun) is short for temporary object. Sometimes the standalone term value is used to refer to a temporary object of built-in type. These terms apply to objects, not to expressions.
The C++17 standard is more consistent in object terminology than past standards, e.g. see [conv.rval]/1. It now tries to avoid saying value other than in the context value of an expression.
Now, why are there different expression categories? A C++ program is made up of a collection of expressions, joined to each other with operators to make larger expressions; and fitting within a framework of declarative constructs. These expressions create, destroy, and do other manipulations on objects. Programming in C++ could be described as using expressions to perform operations with objects.
The reason that expression categories exist is to provide a framework for using expressions to express operations that the programmer intends. For example way back in the C days (and probably earlier), the language designers figured that 3 = 5; did not make any sense as part of a program so it was decided to limit what sort of expression can appear on the left-hand side of =, and have the compiler report an error if this restriction wasn't followed.
The term lvalue originated in those days, although now with the development of C++ there are a vast range of expressions and contexts where expression categories are useful, not just the left-hand side of an assignment operator.
Here is some valid C++ code: std::string("3") = std::string("5");. This is conceptually no different from 3 = 5;, however it is allowed. The effect is that a temporary object of type std::string and content "3" is created, and then that temporary object is modified to have content "5", and then the temporary object is destroyed. The language could have been designed so that the code 3 = 5; specifies a similar series of events (but it wasn't).
Why is the string example legal but the int example not?
Every expression has to have a category. The category of an expression might not seem to have an obvious reason at first, but the designers of the language have given each expression a category according to what they think is a useful concept to express and what isn't.
It's been decided that the sequence of events in 3 = 5; as described above is not something anyone would want to do, and if someone did write such a thing then they probably made a mistake and meant something else, so the compiler should help out by giving an error message.
Now, the same logic might conclude that std::string("3") = std::string("5") is not something anyone would ever want to do either. However another argument is that for some other class type, T(foo) = x; might actually be a worthwhile operation, e.g. because T might have a destructor that does something. It was decided that banning this usage could be more harmful to a programmer's intentions than good. (Whether that was a good decision or not is debatable; see this question for discussion).
Now we are getting closer to finally address your question :)
Whether or not there is memory or a storage location associated is not the rationale for expression categories any more. In the abstract machine (more explanation of this below), every temporary object (this includes the one created by 3 in x = 3;) exists in memory.
As described earlier in my answer, a program consists of expressions that manipulate objects. Each expression is said to designate or refer to an object.
It's very common for other answers or articles on this topic to make the incorrect claim that an rvalue can only designate a temporary object, or even worse , that an rvalue is a temporary object , or that a temporary object is an rvalue. An expression is not an object, it is something that occurs in source code for manipulating objects!
In fact a temporary object can be designated by an lvalue or an rvalue expression; and a non-temporary object can be designated by an lvalue or an rvalue expression. They are separate concepts.
Now, there's an expression category rule that you can't apply & to an expression of the rvalue category. The purpose of this rule and these categories is to avoid errors where a temporary object is used after it is destroyed. For example:
int *p = &5; // not allowed due to category rules
*p = 6; // oops, dangling pointer
But you could get around this:
template<typename T> auto f(T&&t) -> T& { return t; }
// ...
int *p = f(5); // Allowed
*p = 6; // Oops, dangling pointer, no compiler error message.
In this latter code, f(5) and *p are both lvalues that designate a temporary object. This is a good example of why the expression category rules exist; by following the rules without a tricky workaround, then we would get an error for the code that tries to write through a dangling pointer.
Note that you can also use this f to find the memory address of a temporary object, e.g. std::cout << &f(5);
In summary, the questions you actually ask all mistakenly conflate expressions with objects. So they are non-questions in that sense. Temporaries are not lvalues, because objects are not expressions.
A valid but related question would be: "Why is the expression that creates a temporary object an rvalue (as opposed to being an lvalue?)"
To which the answer is as was discussed above: having it be an lvalue would increase the risk of creating dangling pointers or dangling references; and as in 3 = 5;, would increase the risk of specifying redundant operations that the programmer probably didn't intend.
I repeat again that the expression categories are a design decision to help with programmer expressiveness; not anything to do with memory or storage locations.
Finally, to the abstract machine and the as-if rule. C++ is defined in terms of an abstract machine, in which temporary objects have storage and addresses too. I gave an example earlier of how to print the address of a temporary object.
The as-if rule says that the output of the actual executable the compiler produces must only match the output that the abstract machine would. The executable doesn't actually have to work in the same way as the abstract machine, it just has to produce the same result.
So for code like x = 5; , even though a temporary object of value 5 has a memory location in the abstract machine; the compiler doesn't have to allocate physical storage on the real machine. It only has to ensure that x ends up having 5 stored in it and there are much easier ways to do this that don't involve extra storage being created.
The as-if rule applies to everything in the program, even though my example here only refers to temporary objects. A non-temporary object could equally well be optimized out, e.g. int x; int y = 5; x = y; // other code that doesn't use y could be changed to int x = 5;.
The same applies for class types without side-effects that would alter the program output. E.g. std::string x = "foo"; std::cout << x; can be optimized to std::cout << "foo"; even though the lvalue x denoted an object with storage in the abstract machine.
There are a lot of common misconceptions in the question and in the other answers; my answer hopes to address that.
The terms lvalue and rvalue are expression categories. They are terms that apply to expressions. Not to objects. (A bit confusingly, the official term for expression categories is "value categories" ! )
The term temporary object refers to objects. This includes objects of class type, as well as objects of built-in type. The term temporary (used as a noun) is short for temporary object. Sometimes the standalone term value is used to refer to a temporary object of built-in type. These terms apply to objects, not to expressions.
The C++17 standard is more consistent in object terminology than past standards, e.g. see [conv.rval]/1. It now tries to avoid saying value other than in the context value of an expression.
Now, why are there different expression categories? A C++ program is made up of a collection of expressions, joined to each other with operators to make larger expressions; and fitting within a framework of declarative constructs. These expressions create, destroy, and do other manipulations on objects. Programming in C++ could be described as using expressions to perform operations with objects.
The reason that expression categories exist is to provide a framework for using expressions to express operations that the programmer intends. For example way back in the C days (and probably earlier), the language designers figured that 3 = 5; did not make any sense as part of a program so it was decided to limit what sort of expression can appear on the left-hand side of =, and have the compiler report an error if this restriction wasn't followed.
The term lvalue originated in those days, although now with the development of C++ there are a vast range of expressions and contexts where expression categories are useful, not just the left-hand side of an assignment operator.
Here is some valid C++ code: std::string("3") = std::string("5");. This is conceptually no different from 3 = 5;, however it is allowed. The effect is that a temporary object of type std::string and content "3" is created, and then that temporary object is modified to have content "5", and then the temporary object is destroyed. The language could have been designed so that the code 3 = 5; specifies a similar series of events (but it wasn't).
Why is the string example legal but the int example not?
Every expression has to have a category. The category of an expression might not seem to have an obvious reason at first, but the designers of the language have given each expression a category according to what they think is a useful concept to express and what isn't.
It's been decided that the sequence of events in 3 = 5; as described above is not something anyone would want to do, and if someone did write such a thing then they probably made a mistake and meant something else, so the compiler should help out by giving an error message.
Now, the same logic might conclude that std::string("3") = std::string("5") is not something anyone would ever want to do either. However another argument is that for some other class type, T(foo) = x; might actually be a worthwhile operation, e.g. because T might have a destructor that does something. It was decided that banning this usage could be more harmful to a programmer's intentions than good. (Whether that was a good decision or not is debatable; see this question for discussion).
Now we are getting closer to finally address your question :)
Whether or not there is memory or a storage location associated is not the rationale for expression categories any more. In the abstract machine (more explanation of this below), every temporary object (this includes the one created by 3 in x = 3;) exists in memory.
As described earlier in my answer, a program consists of expressions that manipulate objects. Each expression is said to designate or refer to an object.
It's very common for other answers or articles on this topic to make the incorrect claim that an rvalue can only designate a temporary object, or even worse , that an rvalue is a temporary object , or that a temporary object is an rvalue. An expression is not an object, it is something that occurs in source code for manipulating objects!
In fact a temporary object can be designated by an lvalue or an rvalue expression; and a non-temporary object can be designated by an lvalue or an rvalue expression. They are separate concepts.
Now, there's an expression category rule that you can't apply & to an expression of the rvalue category. The purpose of this rule and these categories is to avoid errors where a temporary object is used after it is destroyed. For example:
int *p = &5; // not allowed due to category rules
*p = 6; // oops, dangling pointer
But you could get around this:
template<typename T> auto f(T&&t) -> T& { return t; }
// ...
int *p = f(5); // Allowed
*p = 6; // Oops, dangling pointer, no compiler error message.
In this latter code, f(5) and *p are both lvalues that designate a temporary object. This is a good example of why the expression category rules exist; by following the rules without a tricky workaround, then we would get an error for the code that tries to write through a dangling pointer.
Note that you can also use this f to find the memory address of a temporary object, e.g. std::cout << &f(5);
In summary, the questions you actually ask all mistakenly conflate expressions with objects. So they are non-questions in that sense. Temporaries are not lvalues, because objects are not expressions.
A valid but related question would be: "Why is the expression that creates a temporary object an rvalue (as opposed to being an lvalue?)"
To which the answer is as was discussed above: having it be an lvalue would increase the risk of creating dangling pointers or dangling references; and as in 3 = 5;, would increase the risk of specifying redundant operations that the programmer probably didn't intend.
I repeat again that the expression categories are a design decision to help with programmer expressiveness; not anything to do with memory or storage locations.
Finally, to the abstract machine and the as-if rule. C++ is defined in terms of an abstract machine, in which temporary objects have storage and addresses too. I gave an example earlier of how to print the address of a temporary object.
The as-if rule says that the output of the actual executable the compiler produces must only match the output that the abstract machine would. The executable doesn't actually have to work in the same way as the abstract machine, it just has to produce the same result.
So for code like x = 5; , even though a temporary object of value 5 has a memory location in the abstract machine; the compiler doesn't have to allocate physical storage on the real machine. It only has to ensure that x ends up having 5 stored in it and there are much easier ways to do this that don't involve extra storage being created.
The as-if rule applies to everything in the program, even though my example here only refers to temporary objects. A non-temporary object could equally well be optimized out, e.g. int x; int y = 5; x = y; // other code that doesn't use y could be changed to int x = 5;.
The same applies for class types without side-effects that would alter the program output. E.g. std::string x = "foo"; std::cout << x; can be optimized to std::cout << "foo"; even though the lvalue x denoted an object with storage in the abstract machine.
answered Feb 13 at 0:01
M.MM.M
106k11119242
106k11119242
+1 I always assumed the l and r in lvalue/rvalue stood for left and right in the context of an assignment. Is this correct? If so then I would write this very conspicuously in the answer since it's pretty much a very short and to-the-point answer to the question.
– Mehrdad
Feb 13 at 3:17
@Mehrdad, that is where the names come from, and that is what they originally meant, but then X3J11 introduced the notion of a modifiable lvalue and things got murkier.
– prl
Feb 13 at 4:38
@Mehrdad my second section addresses that (and I don't feel your suggestion would be a correct answer, let alone a complete one)
– M.M
Feb 13 at 5:32
Another example of why it makes no sense to allow taking the address of a value:assert(&5 == &5);– assuming that&5was a legal expression, would or should this hold? Would it even be useful in some way to ask whether&5 == &5? I for one cannot possibly imagine what the point would be.
– Arne Vogel
Feb 13 at 14:55
Nice answer (+1) but very easy to get lost (maybe it's just me) so I have the following observations 1)lvalues andrvalues are expression categories, not strictly relate to temporary or non-temporary object andIn fact a temporary object can be designated by an lvalue or an rvalue expression; and a non-temporary object can be designated by an lvalue or an rvalue expression.It is clear from your answer whatlvaluesandrvaluesare not, but could you point out (more clearly maybe) what they are then? 2) Can you explain the syntax-> T& { return t; }?
– Francesco Boi
Feb 18 at 15:28
|
show 5 more comments
+1 I always assumed the l and r in lvalue/rvalue stood for left and right in the context of an assignment. Is this correct? If so then I would write this very conspicuously in the answer since it's pretty much a very short and to-the-point answer to the question.
– Mehrdad
Feb 13 at 3:17
@Mehrdad, that is where the names come from, and that is what they originally meant, but then X3J11 introduced the notion of a modifiable lvalue and things got murkier.
– prl
Feb 13 at 4:38
@Mehrdad my second section addresses that (and I don't feel your suggestion would be a correct answer, let alone a complete one)
– M.M
Feb 13 at 5:32
Another example of why it makes no sense to allow taking the address of a value:assert(&5 == &5);– assuming that&5was a legal expression, would or should this hold? Would it even be useful in some way to ask whether&5 == &5? I for one cannot possibly imagine what the point would be.
– Arne Vogel
Feb 13 at 14:55
Nice answer (+1) but very easy to get lost (maybe it's just me) so I have the following observations 1)lvalues andrvalues are expression categories, not strictly relate to temporary or non-temporary object andIn fact a temporary object can be designated by an lvalue or an rvalue expression; and a non-temporary object can be designated by an lvalue or an rvalue expression.It is clear from your answer whatlvaluesandrvaluesare not, but could you point out (more clearly maybe) what they are then? 2) Can you explain the syntax-> T& { return t; }?
– Francesco Boi
Feb 18 at 15:28
+1 I always assumed the l and r in lvalue/rvalue stood for left and right in the context of an assignment. Is this correct? If so then I would write this very conspicuously in the answer since it's pretty much a very short and to-the-point answer to the question.
– Mehrdad
Feb 13 at 3:17
+1 I always assumed the l and r in lvalue/rvalue stood for left and right in the context of an assignment. Is this correct? If so then I would write this very conspicuously in the answer since it's pretty much a very short and to-the-point answer to the question.
– Mehrdad
Feb 13 at 3:17
@Mehrdad, that is where the names come from, and that is what they originally meant, but then X3J11 introduced the notion of a modifiable lvalue and things got murkier.
– prl
Feb 13 at 4:38
@Mehrdad, that is where the names come from, and that is what they originally meant, but then X3J11 introduced the notion of a modifiable lvalue and things got murkier.
– prl
Feb 13 at 4:38
@Mehrdad my second section addresses that (and I don't feel your suggestion would be a correct answer, let alone a complete one)
– M.M
Feb 13 at 5:32
@Mehrdad my second section addresses that (and I don't feel your suggestion would be a correct answer, let alone a complete one)
– M.M
Feb 13 at 5:32
Another example of why it makes no sense to allow taking the address of a value:
assert(&5 == &5); – assuming that &5 was a legal expression, would or should this hold? Would it even be useful in some way to ask whether &5 == &5? I for one cannot possibly imagine what the point would be.– Arne Vogel
Feb 13 at 14:55
Another example of why it makes no sense to allow taking the address of a value:
assert(&5 == &5); – assuming that &5 was a legal expression, would or should this hold? Would it even be useful in some way to ask whether &5 == &5? I for one cannot possibly imagine what the point would be.– Arne Vogel
Feb 13 at 14:55
Nice answer (+1) but very easy to get lost (maybe it's just me) so I have the following observations 1)
lvalues and rvalues are expression categories, not strictly relate to temporary or non-temporary object and In fact a temporary object can be designated by an lvalue or an rvalue expression; and a non-temporary object can be designated by an lvalue or an rvalue expression. It is clear from your answer what lvalues and rvalues are not, but could you point out (more clearly maybe) what they are then? 2) Can you explain the syntax -> T& { return t; }?– Francesco Boi
Feb 18 at 15:28
Nice answer (+1) but very easy to get lost (maybe it's just me) so I have the following observations 1)
lvalues and rvalues are expression categories, not strictly relate to temporary or non-temporary object and In fact a temporary object can be designated by an lvalue or an rvalue expression; and a non-temporary object can be designated by an lvalue or an rvalue expression. It is clear from your answer what lvalues and rvalues are not, but could you point out (more clearly maybe) what they are then? 2) Can you explain the syntax -> T& { return t; }?– Francesco Boi
Feb 18 at 15:28
|
show 5 more comments
lvalue stands for locator value and represents an object that occupies some identifiable location in memory.
The term locator value is also used here:
C
The C programming language followed a similar taxonomy, except that
the role of assignment was no longer significant: C expressions are
categorized between "lvalue expressions" and others (functions and
non-object values), where "lvalue" means an expression that identifies
an object, a "locator value"[4].
Everything that is not an lvalue is by exclusion an rvalue. Every expression is either an lavalue or rvalue.
Originally lvalue term was used in C to indicate values that can stay on the left side of assignment operator. However with the const keywork this changed. Not all lvalues can be assigned to. Those that can are called modifiable lvalues.
And also that literals and temporaries variables are not lvalues, but
no reason is given for this statement.
According to this answer literals can be lvalues in some cases.
literals of scalar types arervaluebecause they are of known size and are very likely to be embedded directly into the machine commands on the given hardware architecture. What would be the memory location of5?- On the contrary, strangely enough, string literals are
lvaluessince they have unpredictable size and there is no other way to represent them apart from as objects in memory.
An lvalue can be converted to an rvalue. For example in the following instructions
int a =5;
int b = 3;
int c = a+b;
the operator + takes two rvalues. So a and b are converted to rvalues before getting summed. Another example of conversion:
int c = 6;
&c = 4; //ERROR: &c is an rvalue
On the contrary you cannot convert an rvalue to an lvalue.
However you can produce a valid lvalue from an rvalue for example:
int arr = {1, 2};
int* p = &arr[0];
*(p + 1) = 10; // OK: p + 1 is an rvalue, but *(p + 1) is an lvalue
In C++11 rvalues reference are related to the move constructor and move assignment operator.
You can find more details in this clear and well-explained post.
1
I added another link in the answer check theCpaaragraph: en.cppreference.com/w/cpp/language/value_category
– Francesco Boi
Feb 12 at 16:29
1
"Hello worldn"has a definite address.
– NathanOliver
Feb 12 at 16:34
2
It's&"Hello worldn"see: coliru.stacked-crooked.com/a/c19f1a7fa216cdc5
– NathanOliver
Feb 12 at 16:36
"On the contrary you cannot convert an rvalue to an lvalue" - yes you can: writetemplate<typename T> auto f(T&& t) -> T& { return t; }, then callingfon an rvalue will yield an lvalue referring to the same object. E.g.cout << &f(5);
– M.M
Feb 12 at 23:05
@M.M Honestly those lines are not really clear to me so I cannot modify my answer to explain them. If you have some reference I will gladly read it and try to modify my answer accordingly. Also feel free to modify my answer yourself if worth it.
– Francesco Boi
Feb 13 at 8:40
|
show 8 more comments
lvalue stands for locator value and represents an object that occupies some identifiable location in memory.
The term locator value is also used here:
C
The C programming language followed a similar taxonomy, except that
the role of assignment was no longer significant: C expressions are
categorized between "lvalue expressions" and others (functions and
non-object values), where "lvalue" means an expression that identifies
an object, a "locator value"[4].
Everything that is not an lvalue is by exclusion an rvalue. Every expression is either an lavalue or rvalue.
Originally lvalue term was used in C to indicate values that can stay on the left side of assignment operator. However with the const keywork this changed. Not all lvalues can be assigned to. Those that can are called modifiable lvalues.
And also that literals and temporaries variables are not lvalues, but
no reason is given for this statement.
According to this answer literals can be lvalues in some cases.
literals of scalar types arervaluebecause they are of known size and are very likely to be embedded directly into the machine commands on the given hardware architecture. What would be the memory location of5?- On the contrary, strangely enough, string literals are
lvaluessince they have unpredictable size and there is no other way to represent them apart from as objects in memory.
An lvalue can be converted to an rvalue. For example in the following instructions
int a =5;
int b = 3;
int c = a+b;
the operator + takes two rvalues. So a and b are converted to rvalues before getting summed. Another example of conversion:
int c = 6;
&c = 4; //ERROR: &c is an rvalue
On the contrary you cannot convert an rvalue to an lvalue.
However you can produce a valid lvalue from an rvalue for example:
int arr = {1, 2};
int* p = &arr[0];
*(p + 1) = 10; // OK: p + 1 is an rvalue, but *(p + 1) is an lvalue
In C++11 rvalues reference are related to the move constructor and move assignment operator.
You can find more details in this clear and well-explained post.
1
I added another link in the answer check theCpaaragraph: en.cppreference.com/w/cpp/language/value_category
– Francesco Boi
Feb 12 at 16:29
1
"Hello worldn"has a definite address.
– NathanOliver
Feb 12 at 16:34
2
It's&"Hello worldn"see: coliru.stacked-crooked.com/a/c19f1a7fa216cdc5
– NathanOliver
Feb 12 at 16:36
"On the contrary you cannot convert an rvalue to an lvalue" - yes you can: writetemplate<typename T> auto f(T&& t) -> T& { return t; }, then callingfon an rvalue will yield an lvalue referring to the same object. E.g.cout << &f(5);
– M.M
Feb 12 at 23:05
@M.M Honestly those lines are not really clear to me so I cannot modify my answer to explain them. If you have some reference I will gladly read it and try to modify my answer accordingly. Also feel free to modify my answer yourself if worth it.
– Francesco Boi
Feb 13 at 8:40
|
show 8 more comments
lvalue stands for locator value and represents an object that occupies some identifiable location in memory.
The term locator value is also used here:
C
The C programming language followed a similar taxonomy, except that
the role of assignment was no longer significant: C expressions are
categorized between "lvalue expressions" and others (functions and
non-object values), where "lvalue" means an expression that identifies
an object, a "locator value"[4].
Everything that is not an lvalue is by exclusion an rvalue. Every expression is either an lavalue or rvalue.
Originally lvalue term was used in C to indicate values that can stay on the left side of assignment operator. However with the const keywork this changed. Not all lvalues can be assigned to. Those that can are called modifiable lvalues.
And also that literals and temporaries variables are not lvalues, but
no reason is given for this statement.
According to this answer literals can be lvalues in some cases.
literals of scalar types arervaluebecause they are of known size and are very likely to be embedded directly into the machine commands on the given hardware architecture. What would be the memory location of5?- On the contrary, strangely enough, string literals are
lvaluessince they have unpredictable size and there is no other way to represent them apart from as objects in memory.
An lvalue can be converted to an rvalue. For example in the following instructions
int a =5;
int b = 3;
int c = a+b;
the operator + takes two rvalues. So a and b are converted to rvalues before getting summed. Another example of conversion:
int c = 6;
&c = 4; //ERROR: &c is an rvalue
On the contrary you cannot convert an rvalue to an lvalue.
However you can produce a valid lvalue from an rvalue for example:
int arr = {1, 2};
int* p = &arr[0];
*(p + 1) = 10; // OK: p + 1 is an rvalue, but *(p + 1) is an lvalue
In C++11 rvalues reference are related to the move constructor and move assignment operator.
You can find more details in this clear and well-explained post.
lvalue stands for locator value and represents an object that occupies some identifiable location in memory.
The term locator value is also used here:
C
The C programming language followed a similar taxonomy, except that
the role of assignment was no longer significant: C expressions are
categorized between "lvalue expressions" and others (functions and
non-object values), where "lvalue" means an expression that identifies
an object, a "locator value"[4].
Everything that is not an lvalue is by exclusion an rvalue. Every expression is either an lavalue or rvalue.
Originally lvalue term was used in C to indicate values that can stay on the left side of assignment operator. However with the const keywork this changed. Not all lvalues can be assigned to. Those that can are called modifiable lvalues.
And also that literals and temporaries variables are not lvalues, but
no reason is given for this statement.
According to this answer literals can be lvalues in some cases.
literals of scalar types arervaluebecause they are of known size and are very likely to be embedded directly into the machine commands on the given hardware architecture. What would be the memory location of5?- On the contrary, strangely enough, string literals are
lvaluessince they have unpredictable size and there is no other way to represent them apart from as objects in memory.
An lvalue can be converted to an rvalue. For example in the following instructions
int a =5;
int b = 3;
int c = a+b;
the operator + takes two rvalues. So a and b are converted to rvalues before getting summed. Another example of conversion:
int c = 6;
&c = 4; //ERROR: &c is an rvalue
On the contrary you cannot convert an rvalue to an lvalue.
However you can produce a valid lvalue from an rvalue for example:
int arr = {1, 2};
int* p = &arr[0];
*(p + 1) = 10; // OK: p + 1 is an rvalue, but *(p + 1) is an lvalue
In C++11 rvalues reference are related to the move constructor and move assignment operator.
You can find more details in this clear and well-explained post.
edited Feb 13 at 14:01
answered Feb 12 at 16:18
Francesco BoiFrancesco Boi
2,61522543
2,61522543
1
I added another link in the answer check theCpaaragraph: en.cppreference.com/w/cpp/language/value_category
– Francesco Boi
Feb 12 at 16:29
1
"Hello worldn"has a definite address.
– NathanOliver
Feb 12 at 16:34
2
It's&"Hello worldn"see: coliru.stacked-crooked.com/a/c19f1a7fa216cdc5
– NathanOliver
Feb 12 at 16:36
"On the contrary you cannot convert an rvalue to an lvalue" - yes you can: writetemplate<typename T> auto f(T&& t) -> T& { return t; }, then callingfon an rvalue will yield an lvalue referring to the same object. E.g.cout << &f(5);
– M.M
Feb 12 at 23:05
@M.M Honestly those lines are not really clear to me so I cannot modify my answer to explain them. If you have some reference I will gladly read it and try to modify my answer accordingly. Also feel free to modify my answer yourself if worth it.
– Francesco Boi
Feb 13 at 8:40
|
show 8 more comments
1
I added another link in the answer check theCpaaragraph: en.cppreference.com/w/cpp/language/value_category
– Francesco Boi
Feb 12 at 16:29
1
"Hello worldn"has a definite address.
– NathanOliver
Feb 12 at 16:34
2
It's&"Hello worldn"see: coliru.stacked-crooked.com/a/c19f1a7fa216cdc5
– NathanOliver
Feb 12 at 16:36
"On the contrary you cannot convert an rvalue to an lvalue" - yes you can: writetemplate<typename T> auto f(T&& t) -> T& { return t; }, then callingfon an rvalue will yield an lvalue referring to the same object. E.g.cout << &f(5);
– M.M
Feb 12 at 23:05
@M.M Honestly those lines are not really clear to me so I cannot modify my answer to explain them. If you have some reference I will gladly read it and try to modify my answer accordingly. Also feel free to modify my answer yourself if worth it.
– Francesco Boi
Feb 13 at 8:40
1
1
I added another link in the answer check the
C paaragraph: en.cppreference.com/w/cpp/language/value_category– Francesco Boi
Feb 12 at 16:29
I added another link in the answer check the
C paaragraph: en.cppreference.com/w/cpp/language/value_category– Francesco Boi
Feb 12 at 16:29
1
1
"Hello worldn" has a definite address.– NathanOliver
Feb 12 at 16:34
"Hello worldn" has a definite address.– NathanOliver
Feb 12 at 16:34
2
2
It's
&"Hello worldn" see: coliru.stacked-crooked.com/a/c19f1a7fa216cdc5– NathanOliver
Feb 12 at 16:36
It's
&"Hello worldn" see: coliru.stacked-crooked.com/a/c19f1a7fa216cdc5– NathanOliver
Feb 12 at 16:36
"On the contrary you cannot convert an rvalue to an lvalue" - yes you can: write
template<typename T> auto f(T&& t) -> T& { return t; } , then calling f on an rvalue will yield an lvalue referring to the same object. E.g. cout << &f(5);– M.M
Feb 12 at 23:05
"On the contrary you cannot convert an rvalue to an lvalue" - yes you can: write
template<typename T> auto f(T&& t) -> T& { return t; } , then calling f on an rvalue will yield an lvalue referring to the same object. E.g. cout << &f(5);– M.M
Feb 12 at 23:05
@M.M Honestly those lines are not really clear to me so I cannot modify my answer to explain them. If you have some reference I will gladly read it and try to modify my answer accordingly. Also feel free to modify my answer yourself if worth it.
– Francesco Boi
Feb 13 at 8:40
@M.M Honestly those lines are not really clear to me so I cannot modify my answer to explain them. If you have some reference I will gladly read it and try to modify my answer accordingly. Also feel free to modify my answer yourself if worth it.
– Francesco Boi
Feb 13 at 8:40
|
show 8 more comments
Where do they reside if not in memory?
Of course they reside in memory*, there's no way around it. The question is, can your program determine where exactly in memory do they reside. In other words, is your program allowed to take the address of the thing in question.
In a simple example a = 5 the value of five, or an instruction representing an assignment of the value of five, is somewhere in memory. However, you cannot take the address of five, because int *p = &5 is illegal.
Note that string literals are an exception from the "not an lvalue" rule, because const char *p = "hello" produces an address of a string literal.
* However, it may not necessarily be data memory. In fact, they may not even be represented as a constant in the program memory: for example, an assignment
short a; a = 0xFF00 could be represented as a an assignment of 0xFF in the upper octet, and clearing out the lower octet in memory.
You can take address of anlvalue:&++ior&std::endl
– Dan M.
Feb 12 at 15:40
1
"you cannot take the address of five".const int& v = 5;is legal though.
– Jarod42
Feb 12 at 15:40
8
@Jarod42 That is not taking the address of5though. That materialized a temporaryintobject with the value of5and binds the reference to that object, not to the5.
– NathanOliver
Feb 12 at 15:46
5
OrXOR AX,AX; DEC AH... or lots of other strategies.
– Martin Bonner
Feb 12 at 16:17
add a comment |
Where do they reside if not in memory?
Of course they reside in memory*, there's no way around it. The question is, can your program determine where exactly in memory do they reside. In other words, is your program allowed to take the address of the thing in question.
In a simple example a = 5 the value of five, or an instruction representing an assignment of the value of five, is somewhere in memory. However, you cannot take the address of five, because int *p = &5 is illegal.
Note that string literals are an exception from the "not an lvalue" rule, because const char *p = "hello" produces an address of a string literal.
* However, it may not necessarily be data memory. In fact, they may not even be represented as a constant in the program memory: for example, an assignment
short a; a = 0xFF00 could be represented as a an assignment of 0xFF in the upper octet, and clearing out the lower octet in memory.
You can take address of anlvalue:&++ior&std::endl
– Dan M.
Feb 12 at 15:40
1
"you cannot take the address of five".const int& v = 5;is legal though.
– Jarod42
Feb 12 at 15:40
8
@Jarod42 That is not taking the address of5though. That materialized a temporaryintobject with the value of5and binds the reference to that object, not to the5.
– NathanOliver
Feb 12 at 15:46
5
OrXOR AX,AX; DEC AH... or lots of other strategies.
– Martin Bonner
Feb 12 at 16:17
add a comment |
Where do they reside if not in memory?
Of course they reside in memory*, there's no way around it. The question is, can your program determine where exactly in memory do they reside. In other words, is your program allowed to take the address of the thing in question.
In a simple example a = 5 the value of five, or an instruction representing an assignment of the value of five, is somewhere in memory. However, you cannot take the address of five, because int *p = &5 is illegal.
Note that string literals are an exception from the "not an lvalue" rule, because const char *p = "hello" produces an address of a string literal.
* However, it may not necessarily be data memory. In fact, they may not even be represented as a constant in the program memory: for example, an assignment
short a; a = 0xFF00 could be represented as a an assignment of 0xFF in the upper octet, and clearing out the lower octet in memory.
Where do they reside if not in memory?
Of course they reside in memory*, there's no way around it. The question is, can your program determine where exactly in memory do they reside. In other words, is your program allowed to take the address of the thing in question.
In a simple example a = 5 the value of five, or an instruction representing an assignment of the value of five, is somewhere in memory. However, you cannot take the address of five, because int *p = &5 is illegal.
Note that string literals are an exception from the "not an lvalue" rule, because const char *p = "hello" produces an address of a string literal.
* However, it may not necessarily be data memory. In fact, they may not even be represented as a constant in the program memory: for example, an assignment
short a; a = 0xFF00 could be represented as a an assignment of 0xFF in the upper octet, and clearing out the lower octet in memory.answered Feb 12 at 15:31
dasblinkenlightdasblinkenlight
617k617911209
617k617911209
You can take address of anlvalue:&++ior&std::endl
– Dan M.
Feb 12 at 15:40
1
"you cannot take the address of five".const int& v = 5;is legal though.
– Jarod42
Feb 12 at 15:40
8
@Jarod42 That is not taking the address of5though. That materialized a temporaryintobject with the value of5and binds the reference to that object, not to the5.
– NathanOliver
Feb 12 at 15:46
5
OrXOR AX,AX; DEC AH... or lots of other strategies.
– Martin Bonner
Feb 12 at 16:17
add a comment |
You can take address of anlvalue:&++ior&std::endl
– Dan M.
Feb 12 at 15:40
1
"you cannot take the address of five".const int& v = 5;is legal though.
– Jarod42
Feb 12 at 15:40
8
@Jarod42 That is not taking the address of5though. That materialized a temporaryintobject with the value of5and binds the reference to that object, not to the5.
– NathanOliver
Feb 12 at 15:46
5
OrXOR AX,AX; DEC AH... or lots of other strategies.
– Martin Bonner
Feb 12 at 16:17
You can take address of an
lvalue: &++i or &std::endl– Dan M.
Feb 12 at 15:40
You can take address of an
lvalue: &++i or &std::endl– Dan M.
Feb 12 at 15:40
1
1
"you cannot take the address of five".
const int& v = 5; is legal though.– Jarod42
Feb 12 at 15:40
"you cannot take the address of five".
const int& v = 5; is legal though.– Jarod42
Feb 12 at 15:40
8
8
@Jarod42 That is not taking the address of
5 though. That materialized a temporary int object with the value of 5 and binds the reference to that object, not to the 5.– NathanOliver
Feb 12 at 15:46
@Jarod42 That is not taking the address of
5 though. That materialized a temporary int object with the value of 5 and binds the reference to that object, not to the 5.– NathanOliver
Feb 12 at 15:46
5
5
Or
XOR AX,AX; DEC AH ... or lots of other strategies.– Martin Bonner
Feb 12 at 16:17
Or
XOR AX,AX; DEC AH ... or lots of other strategies.– Martin Bonner
Feb 12 at 16:17
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54653276%2fwhy-are-literals-and-temporary-variables-not-lvalues%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
It would be helpful if there is some good reference explaining lvalue and rvalues
– pasha
Feb 12 at 15:20
7
I find an easier rule of thumb is that lvalues can be named and their address can be taken. Maybe that's what they meant by "things with a defined storage location". They have an address that can be obtained.
– François Andrieux
Feb 12 at 15:20
1
@pasha Value categories.
– François Andrieux
Feb 12 at 15:21
1
temporaries do not need a defined storage location by definition. Consider
a = foo();there is no need to store the result offoo()anywhere but ina– user463035818
Feb 12 at 15:21
17
"I've read that lvalues are 'things with a defined storage location.'" That's an oversimplification, and the problem with oversimplifications is that they are, sometimes, wrong. You should find the proper definition instead.
– Lightness Races in Orbit
Feb 12 at 15:21