Are all temporaries rvalues in C++?
Asked Answered
C

7

34

I have been coding in C++ for past few years. But there is one question that I have not been able to figure out. I want to ask, are all temporaries in C++, rvalues?

If no, can anyone provide me an example where temporary produced in the code is an lvalue?

Cinda answered 27/1, 2010 at 6:43 Comment(4)
Got a link to that discussion, @Prasoon?Tedder
Yes, here is the link. groups.google.co.in/group/comp.lang.c++/browse_thread/thread/…Cinda
I think a better, stronger, question would be "can I assign a value to an object for which I didn't allocate storage?"Pietrek
Pass-by-value function arguments are assignable (unless const) and you don't need to assign storage for them. You probably want another definition of "temporary".Jilly
P
46

No.

The C++ language specification never makes such a straightforward assertion as the one you are asking about. It doesn't say anywhere in the language standard that "all temporary objects are rvalues". Moreover, the question itself is a bit of misnomer, since the property of being an rvalue in the C++ language is not a property of an object, but rather a property of an expression (i.e. a property of its result). This is actually how it is defined in the language specification: for different kinds of expressions it says when the result is an lvalue and when it is an rvalue. Among other things, this actually means that a temporary object can be accessed as an rvalue as well as an lvalue, depending on the specific form of expression that is used to perform the access.

For example, the result of literal 2 + 3 expression is obviously an rvalue, a temporary of type int. We cannot apply the unary & to it since unary & requires an lvalue as its operand

&(2 + 3); // ERROR, lvalue required

However, as we all know, a constant reference can be attached to a temporary object, as in

const int &ri = 2 + 3;

In this case the reference is attached to the temporary, extending the lifetime of the latter. Obviously, once it is done, we have access to that very same temporary as an lvalue ri, since references are always lvalues. For example, we can easily and legally apply the unary & to the reference and obtain a pointer to the temporary

const int *pi = &ri;

with that pointer remaining perfectly valid as long as the temporary persists.

Another obvious example of lvalue access to a temporary object is when we access a temporary object of class type through its this pointer. The result of *this is an lvalue (as is always the case with the result of unary * applied to a data pointer), yet it doesn't change the fact that the actual object might easily be a temporary. For a given class type T, expression T() is an rvalue, as explicitly stated in the language standard, yet the temporary object accessed through *T().get_this() expression (with the obvious implementation of T::get_this()) is an lvalue. Unlike the previous example, this method allows you to immediately obtain a non-const-qualified lvalue, which refers to a temporary object.

So, once again, the very same temporary object might easily be "seen" as an rvalue or as an lvalue depending on what kind of expression (what kind of access path) you use to "look" at that object.

Persson answered 27/1, 2010 at 9:51 Comment(3)
I agreed with this sentence: 'being an rvalue in the C++ language is not a property of an object, but rather a property of an expression'. But then again, the standard is not quite so consistent: 5.2.3/2 defines T() as an expression that 'creates an rvalue of the specified type'. My understanding (english is not my native language) is that 'rvalue' is used as a property of the created instance, and not the expression itself.Hacking
@DavidRodríguez-dribeas In this case, the authors probably wanted to express the fact that rvalues are managed by the compiler (as opposed to lvalues).Warm
Why can we attach the const-reference to a temporary object?Septa
J
10

Prasoon Saurav already linked a very good clc++ thread. In there, James Kanze explains why the question doesn't really make sense. It boils down to:

  • rvalue-ness is a (boolean) property of expressions - each expression is either an lvalue or an rvalue
  • temporaries are not expressions

For that reason, the question doesn't make sense.

A good example is the following code:

int main() {
  const int& ri = 4;
  std::cout << ri << std::endl; 
}

The temporary int with value 4 is not an expression. The expression ri that's printed is not a temporary. It's an lvalue, and refers to a temporary.

Jilly answered 27/1, 2010 at 8:51 Comment(11)
Expressions may be temporaries. For example int(3) = 5 is illegal, but would be legal if the temporary created by int(3) weren't required to be only an rvalue.Pietrek
No, it doesn't work that way. Expressions may create temporaries, or refer to temporaries, but they are not temporaries themselves. They're just different beasts. And to take your example, it's the expression int(3) which is an rvalue.Jilly
You're making an imaginary distinction. Expressions do nothing besides create and refer to values. The evaluation of an expression may result in a handle to a temporary object. It may result in a handle through which assignment is allowed or disallowed. The question of whether a handle to a temporary ever allows assignment is valid.Pietrek
(1) Sorry, but try building a compiler if you can't see the distinction. The distinction is subtle, but not imaginary. (2) That may be a valid question, but it's not the question he asked. And to clarify: the LHS of an assignment expression is an expression itself, and an lvalue expression to be precise.Jilly
(1) Once raw text is turned into an AST, a (well-designed) compiler manipulates a graph of values. If you want to pass a piece of data from the grammar to the code generator, you'd better find a value or a control-flow structure to hang it on. (2) As I said, an expression may yield a handle to an object allowing assignment. Handles to objects which are temporary in the current scope do not allow assignment, although (and this was my error earlier) some lvalues are const and do not allow assignment.Pietrek
And by "handle to an object" I simply mean value, just trying to be clear despite the notion that an expression itself has entity in terms of the program.Pietrek
@Pietrek "the temporary created by int(3)" there is no temporary created hereWarm
@Pietrek "Handles to objects which are temporary in the current scope do not allow assignment" Actually they do: std:string() = std:string("Hello, world")Warm
temporaries are not expressions, but "temporary" is a property of expressions too. When a temporary is first introduced by an expression then that expression is a temporary (that's why the spec sometimes says "an rvalue temporary", because the rvalue expression is a temporary). That's so temporaryness can be tracked at translation time. Lvalues of course can also refer to temporary objects, but the only point (that I know of) where an lvalue itself is a temporary is when throwing an exception. The (synthesized) lvalue expression referring to the temporary exception object is a temporary.Latrinalatrine
@Warm - interesting (and surprising) example, but use :: rather than :Durazzo
@Durazzo Indeed, that would make more sense. ;)Warm
W
1

well, that array operator returns a reference, any function that returns a reference could be considered to do the same thing? all references are const, while they can be lvalues, they modify what they reference, not the reference itself. same is true for the *operator,

*(a temp pointer) = val;

I swear I used to use some compiler that would pass temp values to any function that took a reference,

so you could go:

int Afunc()
{
   return 5;
}

int anotherFunc(int & b)
{
    b = 34;
}


anotherFunc(Afunc());

can't find one that lets you do that now though, the reference has to be const in order to allow passing of temp values.

int anotherFunc(const int & b);

anyway, references can be lvalues and temporary, the trick being the reference it's self is not modified, only what it references.

if you count the-> operator as an operator, then temporary pointers can be lvalues, but the same condition applies, its not the temp pointer that would be changed, but the thing that it points to.

Witkin answered 27/1, 2010 at 7:32 Comment(11)
The tricky part there is whether you can say that a reference is a temporary. I would not consider them to be temporaries, but then again that is just a gut feeling... after all the reference has entity (it exists, takes up space), is unnamed and will disappear right after the execution of the sentence... quite temporary...Hacking
@David: the only question is whether "temporary" is taken to be shorthand for "temporary object." Temporary objects are described by §12.2 and don't include references.Pietrek
@David: Being written out to RAM is immaterial. References go further than RAM or registers, though, because they really aren't supposed to exist like true objects. "The reference is the referent" or just a name for the object, perhaps implemented using a pointer under the hood but not constructed and without even the explicit lifetime that 2+2 has.Pietrek
Does 2+2 yield a temporary?Hacking
Depending on the context, yes. Not if used as an (compile-time) integral constant expression, e.g. as a template argument: typedef foo<2+2> FooFourJilly
The point with the x+2 being considered a temporary is that the standard in section 12.2 does not include it. So if we are to assume that the return of the sum of two integers is a temporary, then not all temporaries are described in 12.2, and that takes us back to whether you can consider a reference to be a temporary... At this point, I agree with @Pietrek in that 'references ARE the referent' and thus there can be no temporary reference...Hacking
due to operator overloads, I would consider all operators ([], +, or whatever) functions, and hence return temporaries. (except ones done at compile time.)Witkin
@DavidRodríguez-dribeas "The tricky part there is whether you can say that a reference is a temporary. I would not consider them to be temporaries" The reference is not the temporary object, but it refers to the temporary object.Warm
@curiousguy: I think we understood the first paragraph in this answer differently, what I understood is that matt was suggesting that all functions returning a reference could be considered to be returning a temporary (the reference) regardless of the fact that the expression is an lvalue expression that refers to the actual object (temporary or not), and that is where I quite doubt that a reference can be considered a temporary.Hacking
@Jilly no 2+2 is never a temporary. It is merely a value. I downvoted some answers because they claimed that things like 2+2 are temporaries. But that does not make sense.Latrinalatrine
@David i recently thought about functions having a reference return type and about casts to references. But I don't think that if you call the function, that a reference is created. What happens is that you just get an lvalue expression, which refers to the object or function that the function returned. The spec does not have the concept of "temporary references" or any such thing. AFAIK, the only situation where unnamed references can occur is in a parameter declaration like void f(int&) { }.Latrinalatrine
A
0

An array indexing operation is both a temporary and an lvalue, something like a[10] = 1 is an example of what you're looking for; the lvalue is a temporary, calculated pointer.

Applecart answered 27/1, 2010 at 7:20 Comment(5)
Beat me to it. An array index is always an lvalue and the results of the calculation to get there is temporary.Uzial
I semi disagree. a[10] is a native instruction in CPUs. It does not create a temporary pointer.Chilopod
The expression (assuming an int array) will not yield a calculated pointer, but rather a reference obtained by dereferencing the pointer. Even if a[10] is not a native type, but a user defined operation (the type of a is a class such that operator[](int) is defined, such as vector), only if it returns a reference (all references are lvalue-s) the operation would compile (assuming that there is some operator=(int) defined for the type of a[10])Hacking
There's no temporary object in that expression. You call a.operator[](int) and it returns a reference to an existing object. Nothing is necessarily created. In the case of map::operator[], a permanent object is created, not a temporary. In the case of vector<bool>::operator[] a temporary object with operator= defined is created, but it's technically not an lvalue.Pietrek
a[10] may be implemented as an indirect addressing mode in the CPU, but that doesn't change the formal existence of a calculated pointer there. However, if references are not technically lvalues... well, ok, point.Applecart
C
0

It depends on what you consider a temporary variable is. You can write something like

#include <stdio.h>
int main()
{
    char carray[10];
    char *c=carray+1;
    *(c+2+4) = 9;
    printf("%d\n",carray[7]);
    return 0;
}

This runs in VisualStudios and GCC. You can run the code in codepad

I consider (c+2+4) a rvalue although i want to assign to it. When i dereference it, it would become an lvalue. So yes all temporaries are rvalues. But you can make rvalues (thus a temporary) into an lvalue by dereferencing it

Chilopod answered 27/1, 2010 at 7:50 Comment(6)
Given a pointer expression E, the result of *E is a lvalue, regardless of whether evaluating E yields a temporary. Note that the result of E is not *E!! the temporary is not the lvalue, but rather a pointer into it.Hacking
"temporary lvalue" hug? There is no room for "considering". (c+2+4) is an rvalue, by definition.Warm
@curiousguy: right. Ok. hmm. Before i edit my answer is *((char*)(ptr+offset))=9 an rvalue? i think this wouldnt compile if i dont deference it... ok so after i deference an rvalue is it a lvalue?Chilopod
In C++, *((char*)(ptr+offset)) is an lvalue, and so is *((char*)(ptr+offset))=9. In C, only the first expression is a lvalue. "i think this wouldnt compile if i dont deference it..." (ptr+offset); would compile as a statement, so would 2+2;. Expressions with no effect whose result is also ignored are legal in C and C++. "ok so after i deference an rvalue is it a lvalue?" Dereferencing a pointer gets you a lvalue.Warm
@curiousguy: great reply. I know 2+2 compiles but i meant (char*)2+2= 0 wouldnt (rvalue being treated as lvalue)Chilopod
You can't dereference an integer!Warm
P
0

Short answer: yes, but I'm not going to quote the standard, because proving the point would require addressing every kind of temporary there is. By definition a temporary has a lifetime of one statement, so assigning things to one would be poor style at best.

Interesting answer: Copy elision can make (often makes) a temporary object identical with an lvalue object. For example,

MyClass blah = MyClass( 3 ); // temporary likely to be optimized out

or

return MyClass( 3 ); // likely to directly initialize object in caller's frame

Edit: as for the question of whether there is any temporary object in those cases, §12.8/15 mentions

the copy operation can be omitted by constructing the temporary object directly into the target of the omitted copy

which would indicate that there is a temporary object which may be identical with an lvalue.

Pietrek answered 27/1, 2010 at 8:42 Comment(1)
"By definition a temporary has a lifetime of one statement" by definition?Warm
W
-2

If no, can anyone provide me an example where temporary produced in the code is an lvalue?

The following code binds a constant reference to a temporary object of type const float created by the compiler:

int i;
const float &cfr = i;

The behaviour is "as if":

int i;
const float __tmp_cfr = i; // introduced by the compiler
const float &cfr = __tmp_cfr;
Warm answered 7/12, 2011 at 9:55 Comment(7)
Still, this example does not show a temporary that is an lvalue. You have an lvalue referring to a temporary, but the temporary (really the expression of promoting i to float) is an rvalue. I hope someone can explain that better than me.Avoidance
@Xeo: Quite right. The terms "rvalue" and "lvalue" refer to expressions, not statements, and this answer isn't explaining any of this. The temporary expression float(i) is indeed an rvalue, which gets bound to the constant reference. A hypothetical expression cfr would be an lvalue.Masterpiece
@Avoidance "really the expression of promoting i to float" I see what you mean, but it's a special race of expressions that don't have a textual (or AST) representation that appears in the source code!Warm
@KerrekSB "this answer isn't explaining any of this" MSalters already explained in his answer that the question does not really make sense, so I didn't feel I had to repeat that. "The temporary expression float(i)" but this expression does not appear in the code fragment, so how can we call that: a virtual expression?Warm
"MSalters already explained in his answer that the question does not really make sense" literally, it does not; but I think I get the idea of what is being asked.Warm
@Curiousguy Arrite man...I was frustrated by your (I assumed) downvote and what I considered a snide comment on our last interaction. Fact is, I answer enough newbie questions sincerely ( see for example: #8352639 ) that I guess I hold anyone who has "real knowledge" to a better standard of communication. But this looks very frustrating, and I offer you a Xmas +1 as a gesture of Goodwill and hope for the future of knowledge and the Internets--anonymous as they may sometimes be. :-/Unpin
@HostileFork "I was frustrated" I was frustrated too by our inability to communicate. "what I considered a snide comment" which comment? ;) "a gesture of Goodwill" Thank you, I appreciate that.Warm

© 2022 - 2024 — McMap. All rights reserved.