class A {}
A foo() {
A a;
// some work
return a;
}
Here it returns an instance of A, and I saw many readings saying that this returns a rvalue.
My confusion is, since it's perfectly legit to do A b; a = b;, the variable a seems to be an lvalue. So why does it become rvalue when it's the returned one?
There is no such thing as an rvalue object.
Rvalue and lvalue refers to expressions.
One expression that refers to an object can be an rvalue, while another that refers to the same object can be an lvalue.
Originally "lvalue" referred to any expression that could be on the left hand side of C's =, while "rvalue" was any expression that could only be on the right side. Since then C++ has acquired const and references, and things have got complicated. But the early C view of things is useful to understand this.
Regarding …
” the variable a seems to be an lvalue. So why does it become rvalue when it's the returned one?
Well, the variable in itself is not an lvalue. The expression a is an lvalue. And the function does not return the variable: it returns the variable's value.
So, as I understand it there are two misconceptions involved in the question:
The idea that lvalue/rvalue is about objects. It's about expressions.
The idea that A foo() { return... } return a reference to an object (as in e.g. Java). It returns a value.
An lvalue is an expression that refers to a memory location and allows us to take the address of that memory location via the & operator. An rvalue is an expression that is not an lvalue.
See here http://thbecker.net/articles/rvalue_references/section_01.html
In your example, you're returning a copy of a stack allocated instance. But that is likely also on the stack and is a 'temporary copy', hence you cannot take the address of it. Hence it's not an lvalue, therefore it's an rvalue
Related
I'm looking at a code snippet which compiles fine;
int a = 10;
int&& b = static_cast<int&&>(a);
The way I'm seeing it, a thing of type T&& is a reference to a temporary -- the underlying object isn't placed in memory.
But a exists in memory. Ergo, how can you have a r-value reference to it?
You're mixing up rvalues and temporaries. Temporaries are objects, rvalues are expressions.
The system of dividing expressions up into the categories lvalue, xvalue, prvalue is designed to help avoid logic errors in the program (for example, preventing 3 = 4; ) but it doesn't have any deeper meaning.
Any object (temporary or not) can be designated by an lvalue expression or an rvalue expression if we really want to . In the case in the question we make an xvalue expression that designates a.
Then we initialize a reference, which binds directly since the type is the same (note: the type , int, not the category).
The code has exactly the same effect as int& b = a; with one single exception, the result of decltype(b).
The comment makes a good point as well -- temporary objects still exist in memory (in the abstract machine) , they are just a different storage class to other objects. And in the case of lifetime-extended temporaries they behave very much like objects of automatic storage.
Going the other way, you can have an lvalue expression designating a temporary object, int const& x = 5; results in the lvalue expression x designating a temporary that has had its lifetime extended.
Or even without lifetime extension, std::string{} = "x" is an lvalue since operator= returns lvalue reference. You could write auto z = (std::string{} = "x") = "y"; which is not a good code style of course but understanding it helps to understand the type and value category system .
Why it is not possible to convert rvalues to lvalues? It is possible to do a conversion in the opposite direction though. Technically rvalues do have a memory address, isn't it?
You can:
int&& x = 3;
x is now an lvalue. A so called 'rvalue-reference' can bind to a temporary,
but anything with a name is an lvalue, so you need to forward<>() it if you need it's rvalueness back.
Note that by binding a temporary to a rvalue-reference (or a const reference) you extend its lifetime. Technically a cast is possible,
but not recommended, since temporaries have short lifetime, so you
typically get a dangling reference.
It is rather straightforward to write a template function unmove(), that does the opposite of std::move():
template<class T> T& unmove(T&& t) { return t; }
Please note that, according to the standard since C++11:
a temporary bound to a reference parameter in a function call exists until the end of the full expression containing that function call: if the function returns a reference, which outlives the full expression, it becomes a dangling reference.
So it is safe to use unmove() within a single full expression, but after the expression has been fully evaluated, the temporaries go away.
My common use for unmove() is to call functions / methods, that return values through references, when I don't need those values.
Correct an anwer above.
int&& x = 3;
x is 'rvalue-reference'. See
https://www.tutorialspoint.com/What-is-double-address-operator-and-and-in-Cplusplus
I am currently reading this tutorial/explanation of rvalue references:
http://thbecker.net/articles/rvalue_references/section_07.html
In the 2nd to last paragraph, the author mentions that "the argument of the copy constructor of T in the body of factory is an lvalue". The code he is referring to is this:
template<typename T, typename Arg>
shared_ptr<T> factory(Arg const & arg)
{
return shared_ptr<T>(new T(arg));
}
I realise that new T(arg) constructs a T object on the heap, but isn't the returned value a temporary pointer value which will be lost if not used (leading to a memory leak I guess), and hence an rvalue?
EDIT: Just to clarify, I understand that there will be no memory leak in this example. I just meant that, if the pointer value would have not been used we would have no way of accessing the constructed T object and hence we'd get a memory leak.
Short answer, I'm sure someone will write a longer one, but:
new gives you a pointer rvalue: new int = nullptr; fails to compile with error about requiring an lvalue.
Dereferencing that pointer gives you an lvalue: *(new int) = 5; will compile (this naive statement also leaks memory because pointer is lost, of course).
The copy constructor takes a reference, so if you have pointer to object, you need to dereference it.
If you lose the pointer, then you can't delete it, so it won't get destructed and the heap memory will not get freed (until the program exits and memory is returned to the OS).
If you put the pointer to some other object which can take ownership of it, like a shared_ptr, then you do not lose the pointer. The other object will delete it according to its semantics, at the latest when the other object (or the last one, in a case of shared ownership, like with shared_ptr) itself gets destructed.
Many contributors here seem to be confused about what value categories are, some going as far as to suggest that shared_ptr "stores an lvalue", which makes no sense at all.
Lvalue and rvalueness doesn't really have anything to do with what something "returns" or with a state of objects in memory; it's about the state of expressions in code. Whether an expression is an lvalue or rvalue (or one of the others) comes from various language rules and constructions.
In short, lvalue expressions are names, and everything else is not an lvalue expression. One notable kind-of exception to this rule is *ptr where ptr is of a pointer type, because *ptr is defined to result in an lvalue for historical reasons (unless operator overloading is involved).
Now, it is not explicitly suggested in the standard that new "returns" (evaluates to) an rvalue, because that statement does not make any sense. new evaluates to a pointer to your new block of memory, and by the rules of the language the expression new T is an rvalue, because that's what rvalue means.
It's hard to explain this better without writing a book, but this is the crux of it.
I realise that new T(arg) constructs a T object on the heap, but isn't the returned value a temporary pointer value which will be lost if not used (leading to a memory leak I guess), and hence an rvalue?
Yes, essentially. The fact that it's a pointer to dynamically-allocated memory is completely irrelevant.
Unfortunately the value category of operators and their operands are underspecified which leaves us in an unfortunate position of having to deduce the value category. Many presume that unless explicitly specified the result is a prvalue.
We can make a pretty educated guess by attempting to use new on the left had side of an assignment and see that it does not work and conclude that it does not yield a lvalue but we are still left in unspecified territory.
We can determine it empirically as this code from Luc Danton answer here does:
template<typename T>
struct value_category {
// Or can be an integral or enum value
static constexpr auto value = "prvalue";
};
template<typename T>
struct value_category<T&> {
static constexpr auto value = "lvalue";
};
template<typename T>
struct value_category<T&&> {
static constexpr auto value = "xvalue";
};
// Double parens for ensuring we inspect an expression,
// not an entity
#define VALUE_CATEGORY(expr) value_category<decltype((expr))>::value
and the result of:
std::cout << VALUE_CATEGORY( new int) << std::endl ;
is:
prvalue
which affirms our conclusion using deduction.
The return of a new is a prvalue not an lvalue, because you cannot write:
new T(arg) = ....; // not legal, so no lvalue
The standard defines (§3.10): An lvalue (so called, historically, because lvalues could appear on the left-hand side of an assignment expression) designates a function or an object. And the return of new designates the address of an object, not the object itself.
If you assign this value to a pointer a and dereference that pointer, or even if you would dereference new return value directly, you make it an lvalue:
*new T(arg) = ....; // valid, althoug you'd loose the address !
*a = ....; // normal poitner dereferencing
This is explained in the standard (§5.3.1/1): The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.
Important: Your edit and shared_ptr
Your example does not leak: shared_ptr<T>(new T(arg)) creates a shared pointer that takes care of your newly created object, and will delete it if it's no longer used.
Two possibility:
the return shared pointer is used in an expression (for example as a temporary shared pointer or assigned to another shared_ptr object): the object is copied and its use count is updated to reflect the number of live references to it. No leak !
the retuned shared pointer is ignored: the returned object is destroyed, its use count is decremented, and as it's not used elsewhere, your object is deleted.
I have tried to compile:
int &&a=3;
int &b=a;
And it work.
I know that "a" is an lvalue, but why i can bind an "rvalue reference to int" to an "lvalue reference to int (not to an rvalue reference to int)"?
This way, i can change the value of the temporary, 3:
b=5 //changing the value of the temporary bound to the rvalue reference
This tecnique is used from std::forward, so i suppose it is a standard behavior.
Is an rvalue reference to int considered as a simple int lvalue storing a temporary?
If not, how do you explain the binding?
References don't bind to other references; they bind to objects.
When you initialise a from 3, you create a new, temporary int object whose lifetime is extended. b is simply another reference to that object.
Note that a is an lvalue, because it is an expression naming an object, even though it has type "rvalue-reference to int"! Be careful not to confuse types with value categories, here: types relate to objects, but value categories relate to expressions (even if those expressions name or otherwise evaluate to some object).
These confusing rules all fit in together quite neatly when you think about it: for the original reference to have been valid, the object's lifetime must have been extended, so b's initialisation is safe.
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?
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.
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.
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.
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.
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.
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
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;