Why is ++i considered an l-value, but i++ is not? - c++

Why is ++i is l-value and i++ not?

Other people have tackled the functional difference between post and pre increment.
As far as being an lvalue is concerned, i++ can't be assigned to because it doesn't refer to a variable. It refers to a calculated value.
In terms of assignment, both of the following make no sense in the same sort of way:
i++ = 5;
i + 0 = 5;
Because pre-increment returns a reference to the incremented variable rather than a temporary copy, ++i is an lvalue.
Preferring pre-increment for performance reasons becomes an especially good idea when you are incrementing something like an iterator object (eg in the STL) that may well be a good bit more heavyweight than an int.

Well as another answerer pointed out already the reason why ++i is an lvalue is to pass it to a reference.
int v = 0;
int const & rcv = ++v; // would work if ++v is an rvalue too
int & rv = ++v; // would not work if ++v is an rvalue
The reason for the second rule is to allow to initialize a reference using a literal, when the reference is a reference to const:
void taking_refc(int const& v);
taking_refc(10); // valid, 10 is an rvalue though!
Why do we introduce an rvalue at all you may ask. Well, these terms come up when building the language rules for these two situations:
We want to have a locator value. That will represent a location which contains a value that can be read.
We want to represent the value of an expression.
The above two points are taken from the C99 Standard which includes this nice footnote quite helpful:
[ The name ‘‘lvalue’’ comes originally
from the assignment expression E1 =
E2, in which the left operand E1 is
required to be a (modifiable) lvalue.
It is perhaps better considered as
representing an object ‘‘locator
value’’. What is sometimes called
‘‘rvalue’’ is in this International
Standard described as the ‘‘value of
an expression’’. ]
The locator value is called lvalue, while the value resulting from evaluating that location is called rvalue. That's right according also to the C++ Standard (talking about the lvalue-to-rvalue conversion):
4.1/2: The value contained in the object
indicated by the lvalue is the rvalue
result.
Conclusion
Using the above semantics, it is clear now why i++ is no lvalue but an rvalue. Because the expression returned is not located in i anymore (it's incremented!), it is just the value that can be of interest. Modifying that value returned by i++ would make not sense, because we don't have a location from which we could read that value again. And so the Standard says it is an rvalue, and it thus can only bind to a reference-to-const.
However, in constrast, the expression returned by ++i is the location (lvalue) of i. Provoking an lvalue-to-rvalue conversion, like in int a = ++i; will read the value out of it. Alternatively, we can make a reference point to it, and read out the value later: int &a = ++i;.
Note also the other occasions where rvalues are generated. For example, all temporaries are rvalues, the result of binary/unary + and minus and all return value expressions that are not references. All those expressions are not located in an named object, but carry rather values only. Those values can of course be backed up by objects that are not constant.
The next C++ Version will include so-called rvalue references that, even though they point to nonconst, can bind to an rvalue. The rationale is to be able to "steal" away resources from those anonymous objects, and avoid copies doing that. Assuming a class-type that has overloaded prefix ++ (returning Object&) and postfix ++ (returning Object), the following would cause a copy first, and for the second case it will steal the resources from the rvalue:
Object o1(++a); // lvalue => can't steal. It will deep copy.
Object o2(a++); // rvalue => steal resources (like just swapping pointers)

It seem that a lot of people are explaining how ++i is an lvalue, but not the why, as in, why did the C++ standards committee put this feature in, especially in light of the fact that C doesn't allow either as lvalues. From this discussion on comp.std.c++, it appears that it is so you can take its address or assign to a reference. A code sample excerpted from Christian Bau's post:
int i;
extern void f (int* p);
extern void g (int& p);
f (&++i); /* Would be illegal C, but C programmers
havent missed this feature */
g (++i); /* C++ programmers would like this to be legal */
g (i++); /* Not legal C++, and it would be difficult to
give this meaningful semantics */
By the way, if i happens to be a built-in type, then assignment statements such as ++i = 10 invoke undefined behavior, because i is modified twice between sequence points.

I'm getting the lvalue error when I try to compile
i++ = 2;
but not when I change it to
++i = 2;
This is because the prefix operator (++i) changes the value in i, then returns i, so it can still be assigned to. The postfix operator (i++) changes the value in i, but returns a temporary copy of the old value, which cannot be modified by the assignment operator.
Answer to original question:
If you're talking about using the increment operators in a statement by themselves, like in a for loop, it really makes no difference. Preincrement appears to be more efficient, because postincrement has to increment itself and return a temporary value, but a compiler will optimize this difference away.
for(int i=0; i<limit; i++)
...
is the same as
for(int i=0; i<limit; ++i)
...
Things get a little more complicated when you're using the return value of the operation as part of a larger statement.
Even the two simple statements
int i = 0;
int a = i++;
and
int i = 0;
int a = ++i;
are different. Which increment operator you choose to use as a part of multi-operator statements depends on what the intended behavior is. In short, no you can't just choose one. You have to understand both.

POD Pre increment:
The pre-increment should act as if the object was incremented before the expression and be usable in this expression as if that happened. Thus the C++ standards comitee decided it can also be used as an l-value.
POD Post increment:
The post-increment should increment the POD object and return a copy for use in the expression (See n2521 Section 5.2.6). As a copy is not actually a variable making it an l-value does not make any sense.
Objects:
Pre and Post increment on objects is just syntactic sugar of the language provides a means to call methods on the object. Thus technically Objects are not restricted by the standard behavior of the language but only by the restrictions imposed by method calls.
It is up to the implementor of these methods to make the behavior of these objects mirror the behavior of the POD objects (It is not required but expected).
Objects Pre-increment:
The requirement (expected behavior) here is that the objects is incremented (meaning dependant on object) and the method return a value that is modifiable and looks like the original object after the increment happened (as if the increment had happened before this statement).
To do this is siple and only require that the method return a reference to it-self. A reference is an l-value and thus will behave as expected.
Objects Post-increment:
The requirement (expected behavior) here is that the object is incremented (in the same way as pre-increment) and the value returned looks like the old value and is non-mutable (so that it does not behave like an l-value).
Non-Mutable:To do this you should return an object. If the object is being used within an expression it will be copy constructed into a temporary variable. Temporary variables are const and thus it will non-mutable and behave as expected.
Looks like the old value:This is simply achieved by creating a copy of the original (probably using the copy constructor) before makeing any modifications. The copy should be a deep copy otherwise any changes to the original will affect the copy and thus the state will change in relationship to the expression using the object.
In the same way as pre-increment:It is probably best to implement post increment in terms of pre-increment so that you get the same behavior.
class Node // Simple Example
{
/*
* Pre-Increment:
* To make the result non-mutable return an object
*/
Node operator++(int)
{
Node result(*this); // Make a copy
operator++(); // Define Post increment in terms of Pre-Increment
return result; // return the copy (which looks like the original)
}
/*
* Post-Increment:
* To make the result an l-value return a reference to this object
*/
Node& operator++()
{
/*
* Update the state appropriatetly */
return *this;
}
};

Regarding LValue
In C (and Perl for instance), neither ++i nor i++ are LValues.
In C++, i++ is not and LValue but ++i is.
++i is equivalent to i += 1, which is equivalent to i = i + 1.
The result is that we're still dealing with the same object i.
It can be viewed as:
int i = 0;
++i = 3;
// is understood as
i = i + 1; // i now equals 1
i = 3;
i++ on the other hand could be viewed as:
First we use the value of i, then increment the object i.
int i = 0;
i++ = 3;
// would be understood as
0 = 3 // Wrong!
i = i + 1;
(edit: updated after a blotched first-attempt).

The main difference is that i++ returns the pre-increment value whereas ++i returns the post-increment value. I normally use ++i unless I have a very compelling reason to use i++ - namely, if I really do need the pre-increment value.
IMHO it is good practise to use the '++i' form. While the difference between pre- and post-increment is not really measurable when you compare integers or other PODs, the additional object copy you have to make and return when using 'i++' can represent a significant performance impact if the object is either quite expensive to copy, or incremented frequently.

By the way - avoid using multiple increment operators on the same variable in the same statement. You get into a mess of "where are the sequence points" and undefined order of operations, at least in C. I think some of that was cleaned up in Java nd C#.

Maybe this has something to do with the way the post-increment is implemented. Perhaps it's something like this:
Create a copy of the original value in memory
Increment the original variable
Return the copy
Since the copy is neither a variable nor a reference to dynamically allocated memory, it can't be a l-value.

How does the compiler translate this expression? a++
We know that we want to return the unincremented version of a, the old version of a before the increment. We also want to increment a as a side effect. In other words, we are returning the old version of a, which no longer represents the current state of a, it no longer is the variable itself.
The value which is returned is a copy of a which is placed into a register. Then the variable is incremented. So here you are not returning the variable itself, but you are returning a copy which is a separate entity! This copy is temporarily stored inside a register and then it is returned. Recall that a lvalue in C++ is an object that has an identifiable location in memory. But the copy is stored inside a register in the CPU, not in memory. All rvalues are objects which do not have an identifiable location in memory. That explains why the copy of the old version of a is an rvalue, because it gets temporarily stored in a register. In general, any copies, temporary values, or the results of long expressions like (5 + a) * b are stored in registers, and then they are assigned into the variable, which is a lvalue.
The postfix operator must store the original value into a register so that it can return the unincremented value as its result.
Consider the following code:
for (int i = 0; i != 5; i++) {...}
This for-loop counts up to five, but i++ is the most interesting part. It is actually two instructions in 1. First we have to move the old value of i into the register, then we increment i. In pseudo-assembly code:
mov i, eax
inc i
eax register now contains the old version of i as a copy. If the variable i resides in the main memory, it might take the CPU a lot of time to go and get the copy all the way from the main memory and move it into the register. That is usually very fast for modern computer systems, but if your for-loop iterates a hundred thousand times, all those extra operations start to add up! It would be a significant performance penalty.
Modern compilers are usually smart enough to optimize away this extra work for integer and pointer types. For more complicated iterator types, or maybe class types, this extra work potentially might be more costly.
What about the prefix increment ++a?
We want to return the incremented version of a, the new version of a after the increment. The new version of a represents the current state of a, because it is the variable itself.
First a is incremented. Since we want to get the updated version of a, why not just return the variable a itself? We do not need to make a temporary copy into the register to generate an rvalue. That would require unnecessary extra work. So we just return the variable itself as an lvalue.
If we don't need the unincremented value, there's no need for the extra work of copying the old version of a into a register, which is done by the postfix operator. That is why you should only use a++ if you really need to return the unincremented value. For all other purposes, just use ++a. By habitually using the prefix versions, we do not have to worry about whether the performance difference matters.
Another advantage of using ++a is that it expresses the intent of the program more directly: I just want to increment a! However, when I see a++ in someone else's code, I wonder why do they want to return the old value? What is it for?

C#:
public void test(int n)
{
Console.WriteLine(n++);
Console.WriteLine(++n);
}
/* Output:
n
n+2
*/

Related

std::atomic address after store

I can't seem to get the address of an atomic object after a store.
e.g.
std::atomic<int> i;
std::atomic<int>* p = &++i; // doesn't work
auto* p = &++i; // doesn't work
// below works:
++i;
auto* p = &i;
What's happening here and why?
To clarify: I know it returns an r-value. Why doesn't it return the original object, this? Is this a purposeful design choice or was it an oversight?
More specifically, what's happening under-the-hood for this requirement?
While the pre-increment operator usually returns its operand by reference, in the case of std::atomic integers, it returns the new value as a temporary. So in your example ++i does not return a reference to the atomic<int> i itself, it returns the new value of i (i.e. an int). You can see this at: https://en.cppreference.com/w/cpp/atomic/atomic/operator_arith
It would be misleading and even dangerous to return a reference to the original atomic<int>, because to access the int value through this reference would require a second, separate read operation — so its value might be different from the value at the time of increment. (This isn't particularly relevant your example code, since you are only trying to obtain a pointer to the referenced object, but some code will actually access the value after ++ so this is why returning a reference isn't possible.)
In other words, if ++i returned a reference to the atomic<int> i, then
int j = ++i;
would be equivalent to
++i;
// ...other threads may modify the value of `i` here!...
int j = i;
The whole point of atomics is to perform reads and writes together as an indivisible operation, so ++i must internally use hardware/OS atomic operations to simultaneously read and increment the integer, so the new value is returned as a temporary.
If you're curious to see what's under the hood, here is libc++'s implementation where you can see that operator++ simply calls into fetch_add(1) and returns the result + 1.

Function Return Mechanism:Temporary object, R-Value, L-Value

Analyzing the question on its low level, when a function returns a value, it is returned either in a cpu register, or in a space allocated on the stack previously by the caller.
At this point the calling function can take the value and copy it into its local variable.
int sum(int a,int b){return a + b;}
int main(){int risultato = sum(10,20);return 0;}
in this case the sum function returns the value in the EAX register. Then the main function copies the value from the eax register into a memory location on the stack.
This is what really happens.
Moving now to the abstraction of C ++, if I tried to do an operation like this:
sum (10.20) = 4;
it gives me an error.
This is because basically the function is not returning the memory location in which the value is contained, but the value itself.
Being therefore an r-value, this will not be assignable, since it is not possible to assign a value to another value.
The issue becomes completely different when the dereferencing operator * is used.
In this case, it will not be returned a value, but the memory location itself (l-value), which will therefore be assignable.
Is what I wrote correct?
Let's take now this second exemple.
class class1 {public: int i; int b; class1(int i,int b) { this->i = i;this->b = b; }};
class1 fun() { class1 c(10,5); return c; }
int main() {fun().i = 4; return 0;}
in this case the function returns an object.
If I try to execute an instruction like this:
fun (). i = 4; I always get an error.
I know that when the function is called a temporary object is created on the stack.
Returning the function an object , but not as a variable (l-value), but as a set of values, it will not be possible to assign one of these with the value 4.
The same problem also seems to exist with this statement here:
class1(10,20).i = 4;
In this case I am creating a temporary object, I don't understand why it doesn't give me the possibility to assign the object's variable i, why in this case is it always interpreted as an r-value and not as an l-value?
I know that what I am doing has no use in practice, but it remains a purely theoretical question, which I need to understand the syntax of language correctly.
Could you comment everything I have said so far, expressing your point of view, and trying to answer the final question?
Moving now to the abstraction of C ++, if I tried to do an operation like this: sum (10.20) = 4; it gives me an error. This is because basically the function is not returning the memory location in which the value is contained, but the value itself. Being therefore an r-value, this will not be assignable, since it is not possible to assign a value to another value. The issue becomes completely different when the dereferencing operator * is used. In this case, it will not be returned a value, but the memory location itself (l-value), which will therefore be assignable.
Is what I wrote correct?
Kind of. You say
This is because basically the function is not returning the memory location in which the value is contained
But that is not what happens. An object is returned, that object has a value. What makes it an rvalue is that the function "returns by value" (another name for makes a temporary object).
Being therefore an r-value, this will not be assignable, since it is not possible to assign a value to another value
This is only true for built in types. The assignment operator of built in types requires that the object being assigned to be an lvalue. If you have a user defined type (class, struct) then you can assign to an rvalue.
In this case I am creating a temporary object, I don't understand why it doesn't give me the possibility to assign the object's variable i, why in this case is it always interpreted as an r-value and not as an l-value?
The reason is that with operator . if the object you call it on is an rvalue, then the member you access is treated as an rvalue. Since i is a built in type, and an rvalue, you can't assign to it.
I know that what I am doing has no use in practice
This is the answer to your question:
Why in this case is it always interpreted as an r-value and not as an l-value?
It's harder to implement the compiler if it needs to make this an L-Value, and since it has no use, it's not worth the trouble.
There are some things that are just for the convenience of compiler writers.
#NathanOliver answered the C++ abstract machine part. I'll just add a note about how that maps to asm.
Then the main function copies the value from the eax register into a memory location on the stack.
Or not, if the optimizing compiler just keeps risultato in a register like EAX. Or optimizes it away completely because in this case it's unused.
In abstract C every object has a memory address (except for register int foo variables), but in practice unless you disable optimization variables only have addresses if the compiler runs out of registers.
The return-value object is in EAX.
Notice that mainstream C++ calling conventions only ever return trivially-copyable objects in registers. A non-trivial constructor or destructor will force even a struct of one member to be returned by hidden pointer, to make sure the constructor and destructor have a consistent this. (Calling convention rules can't depend on the content of the constructor and destructor functions, just whether either is defined at all.)

Returning named rvalue reference [duplicate]

If I have a class A and functions
A f(A &&a)
{
doSomething(a);
return a;
}
A g(A a)
{
doSomething(a);
return a;
}
the copy constructor is called when returning a from f, but the move constructor is used when returning from g. However, from what I understand, f can only be passed an object that it is safe to move (either a temporary or an object marked as moveable, e.g., using std::move). Is there any example when it would not be safe to use the move constructor when returning from f? Why do we require a to have automatic storage duration?
I read the answers here, but the top answer only shows that the spec should not allow moving when passing a to other functions in the function body; it does not explain why moving when returning is safe for g but not for f. Once we get to the return statement, we will not need a anymore inside f.
Update 0
So I understand that temporaries are accessible until the end of the full expression. However, the behavior when returning from f still seems to go against the semantics ingrained into the language that it is safe to move a temporary or an xvalue. For example, if you call g(A()), the temporary is moved into the argument for g even though there could be references to the temporary stored somewhere. The same happens if we call g with an xvalue. Since only temporaries and xvalues bind to rvalue references, it seems like to be consistent about the semantics we should still move a when returning from f, since we know a was passed either a temporary or an xvalue.
Second attempt. Hopefully this is more succinct and clear.
I am going to ignore RVO almost entirely for this discussion. It makes it really confusing as to what should happen sans optimizations - this is just about move vs copy semantics.
To assist this a reference is going to be very helpful here on the sorts of value types in c++11.
When to move?
lvalue
These are never moved. They refer to variables or storage locations that are potentially being referred to elsewhere, and as such should not have their contents transferred to another instance.
prvalue
The above defines them as "expressions that do not have identity". Clearly nothing else can refer to a nameless value so these can be moved.
rvalue
The general case of "right-hand" value, and the only thing that's certain is they can be moved from. They may or may not have a named reference, but if they do it is the last such usage.
xvalue
These are sort of a mix of both - they have identity (are a reference) and they can be moved from. They need not have a named variable. The reason? They are eXpiring values, about to be destroyed. Consider them the 'final reference'. xvalues can only be generated from rvalues which is why/how std::move works in converting lvalues to xvalues (through the result of a function call).
glvalue
Another mutant type with its rvalue cousin, it can be either an xvalue or an lvalue - it has identity but it's unclear if this is the last reference to the variable / storage or not, hence it is unclear if it can or cannot be moved from.
Resolution Order
Where an overload exists that can accept either a const lvalue ref or rvalue ref, and an rvalue is passed, the rvalue is bound otherwise the lvalue version is used. (move for rvalues, copy otherwise).
Where it potentially happens
(assume all types are A where not mentioned)
It only occurs where an object is "initialized from an xvalue of the same type". xvalues bind to rvalues but are not as restricted as pure expressions. In other words, movable things are more than unnamed references, they can also be the 'last' reference to an object with respect to the compiler's awareness.
initialization
A a = std::move(b); // assign-move
A a( std::move(b) ); // construct-move
function argument passing
void f( A a );
f( std::move(b) );
function return
A f() {
// A a exists, will discuss shortly
return a;
}
Why it will not happen in f
Consider this variation on f:
void action1(A & a) {
// alter a somehow
}
void action2(A & a) {
// alter a somehow
}
A f(A && a) {
action1( a );
action2( a );
return a;
}
It is not illegal to treat a as an lvalue within f. Because it is an lvalue it must be a reference, whether explicit or not. Every plain-old variable is technically a reference to itself.
That's where we trip up. Because a is an lvalue for the purposes of f, we are in fact returning an lvalue.
To explicitly generate an rvalue, we must use std::move (or generate an A&& result some other way).
Why it will happen in g
With that under our belts, consider g
A g(A a) {
action1( a ); // as above
action2( a ); // as above
return a;
}
Yes, a is an lvalue for the purposes of action1 and action2. However, because all references to a only exist within g (it's a copy or moved-into copy), it can be considered an xvalue in the return.
But why not in f?
There is no specific magic to &&. Really, you should think of it as a reference first and foremost. The fact that we are demanding an rvalue reference in f as opposed to an lvalue reference with A& does not alter the fact that, being a reference, it must be an lvalue, because the storage location of a is external to f and that's as far as any compiler will be concerned.
The same does not apply in g, where it's clear that a's storage is temporary and exists only when g is called and at no other time. In this case it is clearly an xvalue and can be moved.
rvalue ref vs lvalue ref and safety of reference passing
Suppose we overload a function to accept both types of references. What would happen?
void v( A & lref );
void v( A && rref );
The only time void v( A&& ) will be used per the above ("Where it potentially happens"), otherwise void v( A& ). That is, an rvalue ref will always attempt to bind to an rvalue ref signature before an lvalue ref overload is attempted. An lvalue ref should not ever bind to the rvalue ref except in the case where it can be treated as an xvalue (guaranteed to be destroyed in the current scope whether we want it to or not).
It is tempting to say that in the rvalue case we know for sure that the object being passed is temporary. That is not the case. It is a signature intended for binding references to what appears to be a temporary object.
For analogy, it's like doing int * x = 23; - it may be wrong, but you could (eventually) force it to compile with bad results if you run it. The compiler can't say for sure if you're being serious about that or pulling its leg.
With respect to safety one must consider functions that do this (and why not to do this - if it still compiles at all):
A & make_A(void) {
A new_a;
return new_a;
}
While there is nothing ostensibly wrong with the language aspect - the types work and we will get a reference to somewhere back - because new_a's storage location is inside a function, the memory will be reclaimed / invalid when the function returns. Therefore anything that uses the result of this function will be dealing with freed memory.
Similarly, A f( A && a ) is intended to but is not limited to accepting prvalues or xvalues if we really want to force something else through. That's where std::move comes in, and let's us do just that.
The reason this is the case is because it differs from A f( A & a ) only with respect to which contexts it will be preferred, over the rvalue overload. In all other respects it is identical in how a is treated by the compiler.
The fact that we know that A&& is a signature reserved for moves is a moot point; it is used to determine which version of "reference to A -type parameter" we want to bind to, the sort where we should take ownership (rvalue) or the sort where we should not take ownership (lvalue) of the underlying data (that is, move it elsewhere and wipe the instance / reference we're given). In both cases, what we are working with is a reference to memory that is not controlled by f.
Whether we do or not is not something the compiler can tell; it falls into the 'common sense' area of programming, such as not to use memory locations that don't make sense to use but are otherwise valid memory locations.
What the compiler knows about A f( A && a ) is to not create new storage for a, since we're going to be given an address (reference) to work with. We can choose to leave the source address untouched, but the whole idea here is that by declaring A&& we're telling the compiler "hey! give me references to objects that are about to disappear so I might be able to do something with it before that happens". The key word here is might, and again also the fact that we can explicitly target this function signature incorrectly.
Consider if we had a version of A that, when move-constructing, did not erase the old instance's data, and for some reason we did this by design (let's say we had our own memory allocation functions and knew exactly how our memory model would keep data beyond the lifetime of objects).
The compiler cannot know this, because it would take code analysis to determine what happens to the objects when they're handled in rvalue bindings - it's a human judgement issue at that point. At best the compiler sees 'a reference, yay, no allocating extra memory here' and follows rules of reference passing.
It's safe to assume the compiler is thinking: "it's a reference, I don't need to deal with its memory lifetime inside f, it being a temporary will be removed after f is finished".
In that case, when a temporary is passed to f, the storage of that temporary will disappear as soon as we leave f, and then we're potentially in the same situation as A & make_A(void) - a very bad one.
An issue of semantics...
std::move
The very purpose of std::move is to create rvalue references. By and large what it does (if nothing else) is force the resulting value to bind to rvalues as opposed to lvalues. The reason for this is a return signature of A& prior to rvalue references being available, was ambiguous for things like operator overloads (and other uses surely).
Operators - an example
class A {
// ...
public:
A & operator= (A & rhs); // what is the lifetime of rhs? move or copy intended?
A & operator+ (A & rhs); // ditto
// ...
};
int main() {
A result = A() + A(); // wont compile!
}
Note that this will not accept temporary objects for either operator! Nor does it make sense to do this in the case of object copy operations - why do we need to modify an original object that we are copying, probably in order to have a copy we can modify later. This is the reason we have to declare const A & parameters for copy operators and any situation where a copy is to be taken of the reference, as a guarantee that we are not altering the original object.
Naturally this is an issue with moves, where we must modify the original object to avoid the new container's data being freed prematurely. (hence "move" operation).
To solve this mess along comes T&& declarations, which are a replacement to the above example code, and specifically target references to objects in the situations where the above won't compile. But, we wouldn't need to modify operator+ to be a move operation, and you'd be hard pressed to find a reason for doing so (though you could I think). Again, because of the assumption that addition should not modify the original object, only the left-operand object in the expression. So we can do this:
class A {
// ...
public:
A & operator= (const A & rhs); // copy-assign
A & operator= (A && rhs); // move-assign
A & operator+ (const A & rhs); // don't modify rhs operand
// ...
};
int main() {
A result = A() + A(); // const A& in addition, and A&& for assign
A result2 = A().operator+(A()); // literally the same thing
}
What you should take note of here is that despite the fact that A() returns a temporary, it not only is able to bind to const A& but it should because of the expected semantics of addition (that it does not modify its right operand). The second version of the assignment is clearer why only one of the arguments should be expected to be modified.
It's also clear that a move will occur on the assignment, and no move will occur with rhs in operator+.
Separation of return value semantics and argument binding semantics
The reason that there is only one move above is clear from the function (well, operator) definitions. What's important is we are indeed binding what is clearly an xvalue / rvalue, to what is unmistakably an lvalue in operator+.
I have to stress this point: there is no effective difference in this example in the way that operator+ and operator= refer to their argument. As far as the compiler is concerned, within either's function body the argument is effectively const A& for + and A& for =. The difference is purely in constness. The only way in which A& and A&& differ is to distinguish signatures, not types.
With different signatures come different semantics, it's the compiler's toolkit for distinguishing certain cases where there otherwise is no clear distinction from the code. The behavior of the functions themselves - the code body - may not be able to tell the cases apart either!
Another example of this is operator++(void) vs operator++(int). The former expects to return its underlying value before an increment operation and the latter afterwards. There is no int being passed, it's just so the compiler has two signatures to work with - there is just no other way to specify two identical functions with the same name, and as you may or may not know, it is illegal to overload a function on just the return type for similar reasons of ambiguity.
rvalue variables and other odd situations - an exhaustive test
To understand unambiguously what is happening in f I've put together a smorgasbord of things one "should not attempt but look like they'd work" that forces the compiler's hand on the matter almost exhaustively:
void bad (int && x, int && y) {
x += y;
}
int & worse (int && z) {
return z++, z + 1, 1 + z;
}
int && justno (int & no) {
return worse( no );
}
int num () {
return 1;
}
int main () {
int && a = num();
++a = 0;
a++ = 0;
bad( a, a );
int && b = worse( a );
int && c = justno( b );
++c = (int) 'y';
c++ = (int) 'y';
return 0;
}
g++ -std=gnu++11 -O0 -Wall -c -fmessage-length=0 -o "src\\basictest.o" "..\\src\\basictest.cpp"
..\src\basictest.cpp: In function 'int& worse(int&&)':
..\src\basictest.cpp:5:17: warning: right operand of comma operator has no effect [-Wunused-value]
return z++, z + 1, 1 + z;
^
..\src\basictest.cpp:5:26: error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
return z++, z + 1, 1 + z;
^
..\src\basictest.cpp: In function 'int&& justno(int&)':
..\src\basictest.cpp:8:20: error: cannot bind 'int' lvalue to 'int&&'
return worse( no );
^
..\src\basictest.cpp:4:7: error: initializing argument 1 of 'int& worse(int&&)'
int & worse (int && z) {
^
..\src\basictest.cpp: In function 'int main()':
..\src\basictest.cpp:16:13: error: cannot bind 'int' lvalue to 'int&&'
bad( a, a );
^
..\src\basictest.cpp:1:6: error: initializing argument 1 of 'void bad(int&&, int&&)'
void bad (int && x, int && y) {
^
..\src\basictest.cpp:17:23: error: cannot bind 'int' lvalue to 'int&&'
int && b = worse( a );
^
..\src\basictest.cpp:4:7: error: initializing argument 1 of 'int& worse(int&&)'
int & worse (int && z) {
^
..\src\basictest.cpp:21:7: error: lvalue required as left operand of assignment
c++ = (int) 'y';
^
..\src\basictest.cpp: In function 'int& worse(int&&)':
..\src\basictest.cpp:6:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
..\src\basictest.cpp: In function 'int&& justno(int&)':
..\src\basictest.cpp:9:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
01:31:46 Build Finished (took 72ms)
This is the unaltered output sans build header which you don't need to see :) I will leave it as an exercise to understand the errors found but re-reading my own explanations (particularly in what follows) it should be apparent what each error was caused by and why, imo anyway.
Conclusion - What can we learn from this?
First, note that the compiler treats function bodies as individual code units. This is basically the key here. Whatever the compiler does with a function body, it cannot make assumptions about the behavior of the function that would require the function body to be altered. To deal with those cases there are templates but that's beyond the scope of this discussion - just note that templates generate multiple function bodies to handle different cases, while otherwise the same function body must be re-usable in every case the function could be used.
Second, rvalue types were predominantly envisioned for move operations - a very specific circumstance that was expected to occur in assignment and construction of objects. Other semantics using rvalue reference bindings are beyond the scope of any compiler to deal with. In other words, it's better to think of rvalue references as syntax sugar than actual code. The signature differs in A&& vs A& but the argument type for the purposes of the function body does not, it is always treated as A& with the intention that the object being passed should be modified in some way because const A&, while correct syntactically, would not allow the desired behavior.
I can be very sure at this point when I say that the compiler will generate the code body for f as if it were declared f(A&). Per above, A&& assists the compiler in choosing when to allow binding a mutable reference to f but otherwise the compiler doesn't consider the semantics of f(A&) and f(A&&) to be different with respect to what f returns.
It's a long way of saying: the return method of f does not depend on the type of argument it receives.
The confusion is elision. In reality there are two copies in the returning of a value. First a copy is created as a temporary, then this temporary is assigned to something (or it isn't and remains purely temporary). The second copy is very likely elided via return optimization. The first copy can be moved in g and cannot in f. I expect in a situation where f cannot be elided, there will be a copy then a move from f in the original code.
To override this the temporary must be explicitly constructed using std::move, that is, in the return statement in f. However in g we're returning something that is known to be temporary to the function body of g, hence it is either moved twice, or moved once then elided.
I would suggest compiling the original code with all optimizations disabled and adding in diagnostic messages to copy and move constructors to keep tabs on when and where the values are moved or copied before elision becomes a factor. Even if I'm mistaken, an un-optimized trace of the constructors / operations used would paint an unambiguous picture of what the compiler has done, hopefully it will be apparent why it did what it did as well...
Short story: it only depends on doSomething.
Medium story: if doSomething never change a, then f is safe. It receives a rvalue reference and returns a new temporary moved from there.
Long story: things will go bad as soon as doSomething uses a in a move operation, because a may be in an undefined state before it is used in the return statement - it would be the same in g but at least the conversion to a rvalue reference should be explicit
TL/DR: both f and g are safe as long as there is no move operation inside doSomething. The difference comes that a move will silently executed in f, while it will require an explicit conversion to a rvalue reference (eg with std::move) in g.
Third attempt. The second became very long in the process of explaining every nook and cranny of the situation. But hey, I learned a lot too in the process, which I suppose is the point, no? :) Anyway. I'll re-address the question anew, keeping my longer answer as it in itself is a useful reference but falls short of a 'clear explanation'.
What are we dealing with here?
f and g are not trivial situations. They take time to understand and appreciate the first few times you encounter them. The issues at play are the lifetime of objects, Return Value Optimization, confusion of returning object values, and confusion with overloads of reference types. I'll address each and explain their relevance.
References
First thing's first. What's a reference? Aren't they just pointers without the syntax?
They are, but in an important way they're much more than that. Pointers are literally that, they refer to memory locations in general. There are few if any guarantees about the values located at wherever the pointer is set to. References on the other hand are bound to addresses of real values - values that guarantee to exist for the duration they can be accessed, but may not have a name for them available to be accessed in any other way (such as temporaries).
As a rule of thumb, if you can 'take its address' then you're dealing with a reference, a rather special one known as an lvalue. You can assign to an lvalue. This is why *pointer = 3 works, the operator * creates a reference to the address being pointed to.
This doesn't make the reference any more or less valid than the address it points to, however, references you naturally find in C++ do have this guarantee (as would well-written C++ code) - that they are referring to real values in a way where we don't need to know about its lifetime for the duration of our interactions with them.
Lifetime of Objects
We all should know by now when the c'tors and d'tors will be called for something like this:
{
A temp;
temp.property = value;
}
temp's scope is set. We know exactly when it's created and destroyed. One way we can be sure it's destroyed is because this is impossible:
A & ref_to_temp = temp; // nope
A * ptr_to_temp = &temp; // double nope
The compiler stops us from doing that because very clearly we should not expect that object to still exist. This can arise subtly whenever using references, which is why sometimes people can be found suggesting avoidance of references until you know what you're doing with them (or entirely if they've given up understanding them and just want to move on with their lives).
Scope of Expressions
On the other hand we also have to be mindful that temporaries exist until the outer-most expression they're found in has completed. That means up to the semicolon. An expression existing in the LHS of a comma operator, for example, doesn't get destroyed until the semicolon. Ie:
struct scopetester {
static int counter = 0;
scopetester(){++counter;}
~scopetester(){--counter;}
};
scopetester(), std::cout << scopetester::counter; // prints 1
scopetester(), scopetester(), std::cout << scopetester::counter; // prints 2
This still does not avoid issues of sequencing of execution, you still have to deal with ++i++ and other things - operator precedence and the dreaded undefined behavior that can result when forcing ambiguous cases (eg i++ = ++i). What is important is that all temporaries created exist until the semicolon and no longer.
There are two exceptions - elision / in-place-construction (aka RVO) and reference-assignment-from-temporary.
Returning by value and Elision
What is elision? Why use RVO and similar things? All of these come down under a single term that's far easier to appreciate - "in-place construction". Suppose we were using the result of a function call to initialize or set an object. Eg:
A x (void) {return A();}
A y( x() );
Lets consider the longest possible sequence of events that could happen here.
A new A is constructed in x
The temporary value returned by x() is a new A, initialized using a reference to the previous
A new A - y - is initialized using the temporary value
Where possible, the compiler should re-arrange things so that as few as possible intermediate A's are constructed where it's safe to assume the intermediate is inaccessible or otherwise unnecessary. The question is which of the objects can we do without?
Case #1 is an explicit new object. If we are to avoid this being created, we need to have a reference to an object that already exists. This is the most straightforward one and nothing more needs to be said.
In #2 we cannot avoid constructing some result. After all, we are returning by value. However, there are two important exceptions (not including exceptions themselves which are also affected when thrown): NRVO and RVO. These affect what happens in #3, but there are important consequences and rules regarding #2...
This is due to an interesting quirk of elision:
Notes
Copy elision is the only allowed form of optimization that can change the observable side-effects. Because some compilers do not perform copy elision in every situation where it is allowed (e.g., in debug mode), programs that rely on the side-effects of copy/move constructors and destructors are not portable.
Even when copy elision takes place and the copy-/move-constructor is not called, it must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed.
(Since C++11)
In a return statement or a throw-expression, if the compiler cannot perform copy elision but the conditions for copy elision are met or would be met, except that the source is a function parameter, the compiler will attempt to use the move constructor even if the object is designated by an lvalue; see return statement for details.
And more on that in the return statement notes:
Notes
Returning by value may involve construction and copy/move of a temporary object, unless copy elision is used.
(Since C++11)
If expression is an lvalue expression and the conditions for copy elision are met, or would be met, except that expression names a function parameter, then overload resolution to select the constructor to use for initialization of the returned value is performed twice: first as if expression were an rvalue expression (thus it may select the move constructor or a copy constructor taking reference to const), and if no suitable conversion is available, overload resolution is performed the second time, with lvalue expression (so it may select the copy constructor taking a reference to non-const).
The above rule applies even if the function return type is different from the type of expression (copy elision requires same type)
The compiler is allowed to even chain together multiple elisions. All it means is that two sides of a move / copy that would involve an intermediate object, could potentially be made to refer directly to each-other or even be made to be the same object. We don't know and shouldn't need to know when the compiler chooses to do this - it's an optimization, for one, but importantly you should think of move and copy constructors et al as a "last resort" usage.
We can agree the goal is to reduce the number of unnecessary operations in any optimization, provided the observable behavior is the same. Move and copy constructors are used wherever moves and copy operations happen, so what about when the compiler sees fit to remove a move/copy operation itself as an optimization? Should the functionally unnecessary intermediate objects exist in the final program just for the purposes of their side effects? The way the standard is right now, and compilers, seems to be: no - the move and copy constructors satisfy the how of those operations, not the when or why.
The short version: You have less temporary objects, that you ought to not care about to begin with, so why should you miss them. If you do miss them it may just be that your code relies on intermediate copies and moves to do things beyond their stated purpose and contexts.
Lastly, you need to be aware that the elided object is always stored (and constructed) in the receiving location, not the location of its inception.
Quoting this reference -
Named Return Value Optimization
If a function returns a class type by value, and the return statement's expression is the name of a non-volatile object with automatic storage duration, which isn't the function parameter, or a catch clause parameter, and which has the same type (ignoring top-level cv-qualification) as the return type of the function, then copy/move is omitted. When that local object is constructed, it is constructed directly in the storage where the function's return value would otherwise be moved or copied to. This variant of copy elision is known as NRVO, "named return value optimization".
Return Value Optimization
When a nameless temporary, not bound to any references, would be moved or copied into an object of the same type (ignoring top-level cv-qualification), the copy/move is omitted. When that temporary is constructed, it is constructed directly in the storage where it would otherwise be moved or copied to. When the nameless temporary is the argument of a return statement, this variant of copy elision is known as RVO, "return value optimization".
Lifetime of References
One thing we should not do, is this:
A & func() {
A result;
return result;
}
While tempting because it would avoid implicit copying of anything (we're just passing an address right?) it's also a short-sighted approach. Remember the compiler above preventing something looking like this with temp? Same thing here - result is gone once we're done with func, it could be reclaimed and could be anything now.
The reason we cannot is because we cannot pass an address to result out of func - whether as reference or as pointer - and consider it valid memory. We would get no further passing A* out.
In this situation it is best to use an object-copy return type and rely on moves, elision or both to occur as the compiler finds suitable. Always think of copy and move constructors as 'measures of last resort' - you should not rely on the compiler to use them because the compiler can find ways to avoid copy and move operations entirely, and is allowed to do so even if it means the side effects of those constructors wouldn't happen any more.
There is however a special case, alluded to earlier.
Recall that references are guarantees to real values. This implies that the first occurrence of the reference initializes the object and the last (as far as known at compile time) destroys it when going out of scope.
Broadly this covers two situations: when we return a temporary from a function. and when we assign from a function result. The first, returning a temporary, is basically what elision does but you can in effect elide explicitly with reference passing - like passing a pointer in a call chain. It constructs the object at the time of return, but what changes is the object is no longer destroyed after leaving scope (the return statement). And on the other end the second kind happens - the variable storing the result of the function call now has the honor of destroying the value when it goes out of scope.
The important point here is that elision and reference passing are related concepts. You can emulate elision by using pointers to uninitialized variables' storage location (of known type), for example, as you can with reference passing semantics (basically what they're for).
Overloads of Reference Types
References allow us to treat non-local variables as if they are local variables - to take their address, write to that address, read from that address, and importantly, be able to destroy the object at the right time - when the address can no longer be reached by anything.
Regular variables when they leave scope, have their only reference to them disappear, and are promptly destroyed at that time. Reference variables can refer to regular variables, but except for elision / RVO circumstances they do not affect the scope of the original object - not even if the object they referred to goes out of scope early, which can happen if you make references to dynamic memory and are not careful to manage those references yourself.
This means you can capture the results of an expression explicitly by reference. How? Well, this may seem odd at first but if you read the above it will make sense why this works:
class A {
/* assume rule-of-5 (inc const-overloads) has been followed but unless
* otherwise noted the members are private */
public:
A (void) { /* ... */ }
A operator+ ( const A & rhs ) {
A res;
// do something with `res`
return res;
}
};
A x = A() + A(); // doesn't compile
A & y = A() + A(); // doesn't compile
A && z = A() + A(); // compiles
Why? What's going on?
A x = ... - we can't because constructors and assignment is private.
A & y = ... - we can't because we're returning a value, not a reference to a value who's scope is greater or equal to our current scope.
A && z = ... - we can because we're able to refer to xvalues. As consequence of this assignment the lifetime of the temporary value is extended to this capturing lvalue because it in effect has become an lvalue reference. Sound familiar? It's explicit elision if I were to call it anything. This is more apparent when you consider this syntax must involve a new value and must involve assigning that value to a reference.
In all three cases when all constructors and assignment is made public, there is always only three objects constructed, with the address of res always matching the variable storing the result. (on my compiler anyway, optimizations disabled, -std=gnu++11, g++ 4.9.3).
Which means the differences really do come down to just the storage duration of function arguments themselves. Elision and move operations cannot happen on anything but pure expressions, expiring values, or explicit targeting of the "expiring values" reference overload Type&&.
Re-examining f and g
I've annotated the situation in both functions to get things rolling, a shortlist of assumptions the compiler would note when generating (reusable) code for each.
A f( A && a ) {
// has storage duration exceeding f's scope.
// already constructed.
return a;
// can be elided.
// must be copy-constructed, a exceeds f's scope.
}
A g( A a ) {
// has storage duration limited to this function's scope.
// was just constructed somehow, whether by elision, move or copy.
return a;
// elision may occur.
// can move-construct if can't elide.
// can copy-construct if can't move.
}
What we can say for sure about f's a is that it's expecting to capture moved or expression-type values. Because f can accept either expression-references (prvalues) or lvalue-references about to disappear (xvalues) or moved lvalue-references (converted to xvalues via std::move), and because f must be homogenous in the treatment of a for all three cases, a is seen as a reference first and foremost to an area of memory who's lifetime exists for longer than a call to f. That is, it is not possible to distinguish which of the three cases we called f with from within f, so the compiler assumes the longest storage duration it needs for any of the cases, and finds it safest not to assume anything about the storage duration of a's data.
Unlike the situation in g. Here, a - however it happens upon its value - will cease to be accessible beyond a call to g. As such returning it is tantamount to moving it, since it's seen as an xvalue in that case. We could still copy it or more probably even elide it, it can depend on which is allowed / defined for A at the time.
The issues with f
// we can't tell these apart.
// `f` when compiled cannot assume either will always happen.
// case-by-case optimizations can only happen if `f` is
// inlined into the final code and then re-arranged, or if `f`
// is made into a template to specifically behave differently
// against differing types.
A case_1() {
// prvalues
return f( A() + A() );
}
A make_case_2() {
// xvalues
A temp;
return temp;
}
A case_2 = f( make_case_2() )
A case_3(A & other) {
// lvalues
return f( std::move( other ) );
}
Because of the ambiguity of usage the compiler and standards are designed to make f usable consistently in all cases. There can be no assumptions that A&& will always be a new expression or that you will only use it with std::move for its argument etc. Once f is made external to your code, leaving only its call signature, that cannot be the excuse anymore. The function signature - which reference overload to target - is a clue to what the function should be doing with it and how much (or little) it can assume about the context.
rvalue references are not a panacea for targeting only "moved values", they can target a good deal more things and even be targeted incorrectly or unexpectedly if you assume that's all they do. A reference to anything in general should be expected to and be made to exist for longer than the reference does, with the one exception being rvalue reference variables.
rvalue reference variables are in essence, elision operators. Wherever they exist there is in-place construction going on of some description.
As regular variables, they extend the scope of any xvalue or rvalue they receive - they hold the result of the expression as it's constructed rather than by move or copy, and from thereon are equivalent to regular reference variables in usage.
As function variables they can also elide and construct objects in-place, but there is a very important difference between this:
A c = f( A() );
and this:
A && r = f( A() );
The difference is there is no guarantee that c will be move-constructed vs elided, but r definitely will be elided / constructed in-place at some point, owing to the nature of what we're binding to. For this reason we can only assign to r in situations where there will be a new temporary value created.
But why is A&&a not destroyed if it is captured?
Consider this:
void bad_free(A && a) {
A && clever = std::move( a );
// 'clever' should be the last reference to a?
}
This won't work. The reason is subtle. a's scope is longer, and rvalue reference assignments can only extend the lifetime, not control it. clever exists for less time than a, and therefore is not an xvalue itself (unless using std::move again, but then you're back to the same situation, and it continues forth etc).
lifetime extension
Remember that what makes lvalues different to rvalues is that they cannot be bound to objects that have less lifetime than themselves. All lvalue references are either the original variable or a reference that has less lifetime than the original.
rvalues allow binding to reference variables that have longer lifetime than the original value - that's half the point. Consider:
A r = f( A() ); // v1
A && s = f( A() ); // v2
What happens? In both cases f is given a temporary value that outlives the call, and a result object (because f returns by value) is constructed somehow (it will not matter as you shall see). In v1 we are constructing a new object r using the temporary result - we can do this in three ways: move, copy, elide. In v2 we are not constructing a new object, we are extending the lifetime of the result of f to the scope of s, alternatively saying the same: s is constructed in-place using f and therefore the temporary returned by f has its lifetime extended rather than being moved or copied.
The main distinction is v1 requires move and copy constructors (at least one) to be defined even if the process is elided. For v2 you are not invoking constructors and are explicitly saying you want to reference and/or extend the lifetime of a temporary value, and because you don't invoke move or copy constructors the compiler can only elide / construct in-place!
Remember that this has nothing to do with the argument given to f. It works identically with g:
A r = g( A() ); // v1
A && s = g( A() ); // v2
g will create a temporary for its argument and move-construct it using A() for both cases. It like f also constructs a temporary for its return value, but it can use an xvalue because the result is constructed using a temporary (temporary to g). Again, this will not matter because in v1 we have a new object that could be copy-constructed or move-constructed (either is required but not both) while in v2 we are demanding reference to something that's constructed but will disappear if we don't catch it.
Explicit xvalue capture
Example to show this is possible in theory (but useless):
A && x (void) {
A temp;
// return temp; // even though xvalue, can't do this
return std::move(temp);
}
A && y = x(); // y now refers to temp, which is destroyed
Which object does y refer to? We have left the compiler no choice: y must refer to the result of some function or expression, and we've given it temp which works based on type. But no move has occurred, and temp will be deallocated by the time we use it via y.
Why didn't lifetime extension kick in for temp like it did for a in g / f? Because of what we're returning: we can't specify a function to construct things in-place, we can specify a variable to be constructed in place. It also goes to show that the compiler does not look across function / call boundaries to determine lifetime, it will just look at which variables are on the calling side or local, how they're assigned to and how they're initialized if local.
If you want to clear all doubts, try passing this as an rvalue reference: std::move(*(new A)) - what should happen is that nothing should ever destroy it, because it isn't on the stack and because rvalue references do not alter the lifetime of anything but temporary objects (ie, intermediates / expressions). xvalues are candidates for move construction / move assignment and can't be elided (already constructed) but all other move / copy operations can in theory be elided on the whim of the compiler; when using rvalue references the compiler has no choice but to elide or pass on the address.

Returning an argument passed by rvalue reference

If I have a class A and functions
A f(A &&a)
{
doSomething(a);
return a;
}
A g(A a)
{
doSomething(a);
return a;
}
the copy constructor is called when returning a from f, but the move constructor is used when returning from g. However, from what I understand, f can only be passed an object that it is safe to move (either a temporary or an object marked as moveable, e.g., using std::move). Is there any example when it would not be safe to use the move constructor when returning from f? Why do we require a to have automatic storage duration?
I read the answers here, but the top answer only shows that the spec should not allow moving when passing a to other functions in the function body; it does not explain why moving when returning is safe for g but not for f. Once we get to the return statement, we will not need a anymore inside f.
Update 0
So I understand that temporaries are accessible until the end of the full expression. However, the behavior when returning from f still seems to go against the semantics ingrained into the language that it is safe to move a temporary or an xvalue. For example, if you call g(A()), the temporary is moved into the argument for g even though there could be references to the temporary stored somewhere. The same happens if we call g with an xvalue. Since only temporaries and xvalues bind to rvalue references, it seems like to be consistent about the semantics we should still move a when returning from f, since we know a was passed either a temporary or an xvalue.
Second attempt. Hopefully this is more succinct and clear.
I am going to ignore RVO almost entirely for this discussion. It makes it really confusing as to what should happen sans optimizations - this is just about move vs copy semantics.
To assist this a reference is going to be very helpful here on the sorts of value types in c++11.
When to move?
lvalue
These are never moved. They refer to variables or storage locations that are potentially being referred to elsewhere, and as such should not have their contents transferred to another instance.
prvalue
The above defines them as "expressions that do not have identity". Clearly nothing else can refer to a nameless value so these can be moved.
rvalue
The general case of "right-hand" value, and the only thing that's certain is they can be moved from. They may or may not have a named reference, but if they do it is the last such usage.
xvalue
These are sort of a mix of both - they have identity (are a reference) and they can be moved from. They need not have a named variable. The reason? They are eXpiring values, about to be destroyed. Consider them the 'final reference'. xvalues can only be generated from rvalues which is why/how std::move works in converting lvalues to xvalues (through the result of a function call).
glvalue
Another mutant type with its rvalue cousin, it can be either an xvalue or an lvalue - it has identity but it's unclear if this is the last reference to the variable / storage or not, hence it is unclear if it can or cannot be moved from.
Resolution Order
Where an overload exists that can accept either a const lvalue ref or rvalue ref, and an rvalue is passed, the rvalue is bound otherwise the lvalue version is used. (move for rvalues, copy otherwise).
Where it potentially happens
(assume all types are A where not mentioned)
It only occurs where an object is "initialized from an xvalue of the same type". xvalues bind to rvalues but are not as restricted as pure expressions. In other words, movable things are more than unnamed references, they can also be the 'last' reference to an object with respect to the compiler's awareness.
initialization
A a = std::move(b); // assign-move
A a( std::move(b) ); // construct-move
function argument passing
void f( A a );
f( std::move(b) );
function return
A f() {
// A a exists, will discuss shortly
return a;
}
Why it will not happen in f
Consider this variation on f:
void action1(A & a) {
// alter a somehow
}
void action2(A & a) {
// alter a somehow
}
A f(A && a) {
action1( a );
action2( a );
return a;
}
It is not illegal to treat a as an lvalue within f. Because it is an lvalue it must be a reference, whether explicit or not. Every plain-old variable is technically a reference to itself.
That's where we trip up. Because a is an lvalue for the purposes of f, we are in fact returning an lvalue.
To explicitly generate an rvalue, we must use std::move (or generate an A&& result some other way).
Why it will happen in g
With that under our belts, consider g
A g(A a) {
action1( a ); // as above
action2( a ); // as above
return a;
}
Yes, a is an lvalue for the purposes of action1 and action2. However, because all references to a only exist within g (it's a copy or moved-into copy), it can be considered an xvalue in the return.
But why not in f?
There is no specific magic to &&. Really, you should think of it as a reference first and foremost. The fact that we are demanding an rvalue reference in f as opposed to an lvalue reference with A& does not alter the fact that, being a reference, it must be an lvalue, because the storage location of a is external to f and that's as far as any compiler will be concerned.
The same does not apply in g, where it's clear that a's storage is temporary and exists only when g is called and at no other time. In this case it is clearly an xvalue and can be moved.
rvalue ref vs lvalue ref and safety of reference passing
Suppose we overload a function to accept both types of references. What would happen?
void v( A & lref );
void v( A && rref );
The only time void v( A&& ) will be used per the above ("Where it potentially happens"), otherwise void v( A& ). That is, an rvalue ref will always attempt to bind to an rvalue ref signature before an lvalue ref overload is attempted. An lvalue ref should not ever bind to the rvalue ref except in the case where it can be treated as an xvalue (guaranteed to be destroyed in the current scope whether we want it to or not).
It is tempting to say that in the rvalue case we know for sure that the object being passed is temporary. That is not the case. It is a signature intended for binding references to what appears to be a temporary object.
For analogy, it's like doing int * x = 23; - it may be wrong, but you could (eventually) force it to compile with bad results if you run it. The compiler can't say for sure if you're being serious about that or pulling its leg.
With respect to safety one must consider functions that do this (and why not to do this - if it still compiles at all):
A & make_A(void) {
A new_a;
return new_a;
}
While there is nothing ostensibly wrong with the language aspect - the types work and we will get a reference to somewhere back - because new_a's storage location is inside a function, the memory will be reclaimed / invalid when the function returns. Therefore anything that uses the result of this function will be dealing with freed memory.
Similarly, A f( A && a ) is intended to but is not limited to accepting prvalues or xvalues if we really want to force something else through. That's where std::move comes in, and let's us do just that.
The reason this is the case is because it differs from A f( A & a ) only with respect to which contexts it will be preferred, over the rvalue overload. In all other respects it is identical in how a is treated by the compiler.
The fact that we know that A&& is a signature reserved for moves is a moot point; it is used to determine which version of "reference to A -type parameter" we want to bind to, the sort where we should take ownership (rvalue) or the sort where we should not take ownership (lvalue) of the underlying data (that is, move it elsewhere and wipe the instance / reference we're given). In both cases, what we are working with is a reference to memory that is not controlled by f.
Whether we do or not is not something the compiler can tell; it falls into the 'common sense' area of programming, such as not to use memory locations that don't make sense to use but are otherwise valid memory locations.
What the compiler knows about A f( A && a ) is to not create new storage for a, since we're going to be given an address (reference) to work with. We can choose to leave the source address untouched, but the whole idea here is that by declaring A&& we're telling the compiler "hey! give me references to objects that are about to disappear so I might be able to do something with it before that happens". The key word here is might, and again also the fact that we can explicitly target this function signature incorrectly.
Consider if we had a version of A that, when move-constructing, did not erase the old instance's data, and for some reason we did this by design (let's say we had our own memory allocation functions and knew exactly how our memory model would keep data beyond the lifetime of objects).
The compiler cannot know this, because it would take code analysis to determine what happens to the objects when they're handled in rvalue bindings - it's a human judgement issue at that point. At best the compiler sees 'a reference, yay, no allocating extra memory here' and follows rules of reference passing.
It's safe to assume the compiler is thinking: "it's a reference, I don't need to deal with its memory lifetime inside f, it being a temporary will be removed after f is finished".
In that case, when a temporary is passed to f, the storage of that temporary will disappear as soon as we leave f, and then we're potentially in the same situation as A & make_A(void) - a very bad one.
An issue of semantics...
std::move
The very purpose of std::move is to create rvalue references. By and large what it does (if nothing else) is force the resulting value to bind to rvalues as opposed to lvalues. The reason for this is a return signature of A& prior to rvalue references being available, was ambiguous for things like operator overloads (and other uses surely).
Operators - an example
class A {
// ...
public:
A & operator= (A & rhs); // what is the lifetime of rhs? move or copy intended?
A & operator+ (A & rhs); // ditto
// ...
};
int main() {
A result = A() + A(); // wont compile!
}
Note that this will not accept temporary objects for either operator! Nor does it make sense to do this in the case of object copy operations - why do we need to modify an original object that we are copying, probably in order to have a copy we can modify later. This is the reason we have to declare const A & parameters for copy operators and any situation where a copy is to be taken of the reference, as a guarantee that we are not altering the original object.
Naturally this is an issue with moves, where we must modify the original object to avoid the new container's data being freed prematurely. (hence "move" operation).
To solve this mess along comes T&& declarations, which are a replacement to the above example code, and specifically target references to objects in the situations where the above won't compile. But, we wouldn't need to modify operator+ to be a move operation, and you'd be hard pressed to find a reason for doing so (though you could I think). Again, because of the assumption that addition should not modify the original object, only the left-operand object in the expression. So we can do this:
class A {
// ...
public:
A & operator= (const A & rhs); // copy-assign
A & operator= (A && rhs); // move-assign
A & operator+ (const A & rhs); // don't modify rhs operand
// ...
};
int main() {
A result = A() + A(); // const A& in addition, and A&& for assign
A result2 = A().operator+(A()); // literally the same thing
}
What you should take note of here is that despite the fact that A() returns a temporary, it not only is able to bind to const A& but it should because of the expected semantics of addition (that it does not modify its right operand). The second version of the assignment is clearer why only one of the arguments should be expected to be modified.
It's also clear that a move will occur on the assignment, and no move will occur with rhs in operator+.
Separation of return value semantics and argument binding semantics
The reason that there is only one move above is clear from the function (well, operator) definitions. What's important is we are indeed binding what is clearly an xvalue / rvalue, to what is unmistakably an lvalue in operator+.
I have to stress this point: there is no effective difference in this example in the way that operator+ and operator= refer to their argument. As far as the compiler is concerned, within either's function body the argument is effectively const A& for + and A& for =. The difference is purely in constness. The only way in which A& and A&& differ is to distinguish signatures, not types.
With different signatures come different semantics, it's the compiler's toolkit for distinguishing certain cases where there otherwise is no clear distinction from the code. The behavior of the functions themselves - the code body - may not be able to tell the cases apart either!
Another example of this is operator++(void) vs operator++(int). The former expects to return its underlying value before an increment operation and the latter afterwards. There is no int being passed, it's just so the compiler has two signatures to work with - there is just no other way to specify two identical functions with the same name, and as you may or may not know, it is illegal to overload a function on just the return type for similar reasons of ambiguity.
rvalue variables and other odd situations - an exhaustive test
To understand unambiguously what is happening in f I've put together a smorgasbord of things one "should not attempt but look like they'd work" that forces the compiler's hand on the matter almost exhaustively:
void bad (int && x, int && y) {
x += y;
}
int & worse (int && z) {
return z++, z + 1, 1 + z;
}
int && justno (int & no) {
return worse( no );
}
int num () {
return 1;
}
int main () {
int && a = num();
++a = 0;
a++ = 0;
bad( a, a );
int && b = worse( a );
int && c = justno( b );
++c = (int) 'y';
c++ = (int) 'y';
return 0;
}
g++ -std=gnu++11 -O0 -Wall -c -fmessage-length=0 -o "src\\basictest.o" "..\\src\\basictest.cpp"
..\src\basictest.cpp: In function 'int& worse(int&&)':
..\src\basictest.cpp:5:17: warning: right operand of comma operator has no effect [-Wunused-value]
return z++, z + 1, 1 + z;
^
..\src\basictest.cpp:5:26: error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
return z++, z + 1, 1 + z;
^
..\src\basictest.cpp: In function 'int&& justno(int&)':
..\src\basictest.cpp:8:20: error: cannot bind 'int' lvalue to 'int&&'
return worse( no );
^
..\src\basictest.cpp:4:7: error: initializing argument 1 of 'int& worse(int&&)'
int & worse (int && z) {
^
..\src\basictest.cpp: In function 'int main()':
..\src\basictest.cpp:16:13: error: cannot bind 'int' lvalue to 'int&&'
bad( a, a );
^
..\src\basictest.cpp:1:6: error: initializing argument 1 of 'void bad(int&&, int&&)'
void bad (int && x, int && y) {
^
..\src\basictest.cpp:17:23: error: cannot bind 'int' lvalue to 'int&&'
int && b = worse( a );
^
..\src\basictest.cpp:4:7: error: initializing argument 1 of 'int& worse(int&&)'
int & worse (int && z) {
^
..\src\basictest.cpp:21:7: error: lvalue required as left operand of assignment
c++ = (int) 'y';
^
..\src\basictest.cpp: In function 'int& worse(int&&)':
..\src\basictest.cpp:6:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
..\src\basictest.cpp: In function 'int&& justno(int&)':
..\src\basictest.cpp:9:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
01:31:46 Build Finished (took 72ms)
This is the unaltered output sans build header which you don't need to see :) I will leave it as an exercise to understand the errors found but re-reading my own explanations (particularly in what follows) it should be apparent what each error was caused by and why, imo anyway.
Conclusion - What can we learn from this?
First, note that the compiler treats function bodies as individual code units. This is basically the key here. Whatever the compiler does with a function body, it cannot make assumptions about the behavior of the function that would require the function body to be altered. To deal with those cases there are templates but that's beyond the scope of this discussion - just note that templates generate multiple function bodies to handle different cases, while otherwise the same function body must be re-usable in every case the function could be used.
Second, rvalue types were predominantly envisioned for move operations - a very specific circumstance that was expected to occur in assignment and construction of objects. Other semantics using rvalue reference bindings are beyond the scope of any compiler to deal with. In other words, it's better to think of rvalue references as syntax sugar than actual code. The signature differs in A&& vs A& but the argument type for the purposes of the function body does not, it is always treated as A& with the intention that the object being passed should be modified in some way because const A&, while correct syntactically, would not allow the desired behavior.
I can be very sure at this point when I say that the compiler will generate the code body for f as if it were declared f(A&). Per above, A&& assists the compiler in choosing when to allow binding a mutable reference to f but otherwise the compiler doesn't consider the semantics of f(A&) and f(A&&) to be different with respect to what f returns.
It's a long way of saying: the return method of f does not depend on the type of argument it receives.
The confusion is elision. In reality there are two copies in the returning of a value. First a copy is created as a temporary, then this temporary is assigned to something (or it isn't and remains purely temporary). The second copy is very likely elided via return optimization. The first copy can be moved in g and cannot in f. I expect in a situation where f cannot be elided, there will be a copy then a move from f in the original code.
To override this the temporary must be explicitly constructed using std::move, that is, in the return statement in f. However in g we're returning something that is known to be temporary to the function body of g, hence it is either moved twice, or moved once then elided.
I would suggest compiling the original code with all optimizations disabled and adding in diagnostic messages to copy and move constructors to keep tabs on when and where the values are moved or copied before elision becomes a factor. Even if I'm mistaken, an un-optimized trace of the constructors / operations used would paint an unambiguous picture of what the compiler has done, hopefully it will be apparent why it did what it did as well...
Short story: it only depends on doSomething.
Medium story: if doSomething never change a, then f is safe. It receives a rvalue reference and returns a new temporary moved from there.
Long story: things will go bad as soon as doSomething uses a in a move operation, because a may be in an undefined state before it is used in the return statement - it would be the same in g but at least the conversion to a rvalue reference should be explicit
TL/DR: both f and g are safe as long as there is no move operation inside doSomething. The difference comes that a move will silently executed in f, while it will require an explicit conversion to a rvalue reference (eg with std::move) in g.
Third attempt. The second became very long in the process of explaining every nook and cranny of the situation. But hey, I learned a lot too in the process, which I suppose is the point, no? :) Anyway. I'll re-address the question anew, keeping my longer answer as it in itself is a useful reference but falls short of a 'clear explanation'.
What are we dealing with here?
f and g are not trivial situations. They take time to understand and appreciate the first few times you encounter them. The issues at play are the lifetime of objects, Return Value Optimization, confusion of returning object values, and confusion with overloads of reference types. I'll address each and explain their relevance.
References
First thing's first. What's a reference? Aren't they just pointers without the syntax?
They are, but in an important way they're much more than that. Pointers are literally that, they refer to memory locations in general. There are few if any guarantees about the values located at wherever the pointer is set to. References on the other hand are bound to addresses of real values - values that guarantee to exist for the duration they can be accessed, but may not have a name for them available to be accessed in any other way (such as temporaries).
As a rule of thumb, if you can 'take its address' then you're dealing with a reference, a rather special one known as an lvalue. You can assign to an lvalue. This is why *pointer = 3 works, the operator * creates a reference to the address being pointed to.
This doesn't make the reference any more or less valid than the address it points to, however, references you naturally find in C++ do have this guarantee (as would well-written C++ code) - that they are referring to real values in a way where we don't need to know about its lifetime for the duration of our interactions with them.
Lifetime of Objects
We all should know by now when the c'tors and d'tors will be called for something like this:
{
A temp;
temp.property = value;
}
temp's scope is set. We know exactly when it's created and destroyed. One way we can be sure it's destroyed is because this is impossible:
A & ref_to_temp = temp; // nope
A * ptr_to_temp = &temp; // double nope
The compiler stops us from doing that because very clearly we should not expect that object to still exist. This can arise subtly whenever using references, which is why sometimes people can be found suggesting avoidance of references until you know what you're doing with them (or entirely if they've given up understanding them and just want to move on with their lives).
Scope of Expressions
On the other hand we also have to be mindful that temporaries exist until the outer-most expression they're found in has completed. That means up to the semicolon. An expression existing in the LHS of a comma operator, for example, doesn't get destroyed until the semicolon. Ie:
struct scopetester {
static int counter = 0;
scopetester(){++counter;}
~scopetester(){--counter;}
};
scopetester(), std::cout << scopetester::counter; // prints 1
scopetester(), scopetester(), std::cout << scopetester::counter; // prints 2
This still does not avoid issues of sequencing of execution, you still have to deal with ++i++ and other things - operator precedence and the dreaded undefined behavior that can result when forcing ambiguous cases (eg i++ = ++i). What is important is that all temporaries created exist until the semicolon and no longer.
There are two exceptions - elision / in-place-construction (aka RVO) and reference-assignment-from-temporary.
Returning by value and Elision
What is elision? Why use RVO and similar things? All of these come down under a single term that's far easier to appreciate - "in-place construction". Suppose we were using the result of a function call to initialize or set an object. Eg:
A x (void) {return A();}
A y( x() );
Lets consider the longest possible sequence of events that could happen here.
A new A is constructed in x
The temporary value returned by x() is a new A, initialized using a reference to the previous
A new A - y - is initialized using the temporary value
Where possible, the compiler should re-arrange things so that as few as possible intermediate A's are constructed where it's safe to assume the intermediate is inaccessible or otherwise unnecessary. The question is which of the objects can we do without?
Case #1 is an explicit new object. If we are to avoid this being created, we need to have a reference to an object that already exists. This is the most straightforward one and nothing more needs to be said.
In #2 we cannot avoid constructing some result. After all, we are returning by value. However, there are two important exceptions (not including exceptions themselves which are also affected when thrown): NRVO and RVO. These affect what happens in #3, but there are important consequences and rules regarding #2...
This is due to an interesting quirk of elision:
Notes
Copy elision is the only allowed form of optimization that can change the observable side-effects. Because some compilers do not perform copy elision in every situation where it is allowed (e.g., in debug mode), programs that rely on the side-effects of copy/move constructors and destructors are not portable.
Even when copy elision takes place and the copy-/move-constructor is not called, it must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed.
(Since C++11)
In a return statement or a throw-expression, if the compiler cannot perform copy elision but the conditions for copy elision are met or would be met, except that the source is a function parameter, the compiler will attempt to use the move constructor even if the object is designated by an lvalue; see return statement for details.
And more on that in the return statement notes:
Notes
Returning by value may involve construction and copy/move of a temporary object, unless copy elision is used.
(Since C++11)
If expression is an lvalue expression and the conditions for copy elision are met, or would be met, except that expression names a function parameter, then overload resolution to select the constructor to use for initialization of the returned value is performed twice: first as if expression were an rvalue expression (thus it may select the move constructor or a copy constructor taking reference to const), and if no suitable conversion is available, overload resolution is performed the second time, with lvalue expression (so it may select the copy constructor taking a reference to non-const).
The above rule applies even if the function return type is different from the type of expression (copy elision requires same type)
The compiler is allowed to even chain together multiple elisions. All it means is that two sides of a move / copy that would involve an intermediate object, could potentially be made to refer directly to each-other or even be made to be the same object. We don't know and shouldn't need to know when the compiler chooses to do this - it's an optimization, for one, but importantly you should think of move and copy constructors et al as a "last resort" usage.
We can agree the goal is to reduce the number of unnecessary operations in any optimization, provided the observable behavior is the same. Move and copy constructors are used wherever moves and copy operations happen, so what about when the compiler sees fit to remove a move/copy operation itself as an optimization? Should the functionally unnecessary intermediate objects exist in the final program just for the purposes of their side effects? The way the standard is right now, and compilers, seems to be: no - the move and copy constructors satisfy the how of those operations, not the when or why.
The short version: You have less temporary objects, that you ought to not care about to begin with, so why should you miss them. If you do miss them it may just be that your code relies on intermediate copies and moves to do things beyond their stated purpose and contexts.
Lastly, you need to be aware that the elided object is always stored (and constructed) in the receiving location, not the location of its inception.
Quoting this reference -
Named Return Value Optimization
If a function returns a class type by value, and the return statement's expression is the name of a non-volatile object with automatic storage duration, which isn't the function parameter, or a catch clause parameter, and which has the same type (ignoring top-level cv-qualification) as the return type of the function, then copy/move is omitted. When that local object is constructed, it is constructed directly in the storage where the function's return value would otherwise be moved or copied to. This variant of copy elision is known as NRVO, "named return value optimization".
Return Value Optimization
When a nameless temporary, not bound to any references, would be moved or copied into an object of the same type (ignoring top-level cv-qualification), the copy/move is omitted. When that temporary is constructed, it is constructed directly in the storage where it would otherwise be moved or copied to. When the nameless temporary is the argument of a return statement, this variant of copy elision is known as RVO, "return value optimization".
Lifetime of References
One thing we should not do, is this:
A & func() {
A result;
return result;
}
While tempting because it would avoid implicit copying of anything (we're just passing an address right?) it's also a short-sighted approach. Remember the compiler above preventing something looking like this with temp? Same thing here - result is gone once we're done with func, it could be reclaimed and could be anything now.
The reason we cannot is because we cannot pass an address to result out of func - whether as reference or as pointer - and consider it valid memory. We would get no further passing A* out.
In this situation it is best to use an object-copy return type and rely on moves, elision or both to occur as the compiler finds suitable. Always think of copy and move constructors as 'measures of last resort' - you should not rely on the compiler to use them because the compiler can find ways to avoid copy and move operations entirely, and is allowed to do so even if it means the side effects of those constructors wouldn't happen any more.
There is however a special case, alluded to earlier.
Recall that references are guarantees to real values. This implies that the first occurrence of the reference initializes the object and the last (as far as known at compile time) destroys it when going out of scope.
Broadly this covers two situations: when we return a temporary from a function. and when we assign from a function result. The first, returning a temporary, is basically what elision does but you can in effect elide explicitly with reference passing - like passing a pointer in a call chain. It constructs the object at the time of return, but what changes is the object is no longer destroyed after leaving scope (the return statement). And on the other end the second kind happens - the variable storing the result of the function call now has the honor of destroying the value when it goes out of scope.
The important point here is that elision and reference passing are related concepts. You can emulate elision by using pointers to uninitialized variables' storage location (of known type), for example, as you can with reference passing semantics (basically what they're for).
Overloads of Reference Types
References allow us to treat non-local variables as if they are local variables - to take their address, write to that address, read from that address, and importantly, be able to destroy the object at the right time - when the address can no longer be reached by anything.
Regular variables when they leave scope, have their only reference to them disappear, and are promptly destroyed at that time. Reference variables can refer to regular variables, but except for elision / RVO circumstances they do not affect the scope of the original object - not even if the object they referred to goes out of scope early, which can happen if you make references to dynamic memory and are not careful to manage those references yourself.
This means you can capture the results of an expression explicitly by reference. How? Well, this may seem odd at first but if you read the above it will make sense why this works:
class A {
/* assume rule-of-5 (inc const-overloads) has been followed but unless
* otherwise noted the members are private */
public:
A (void) { /* ... */ }
A operator+ ( const A & rhs ) {
A res;
// do something with `res`
return res;
}
};
A x = A() + A(); // doesn't compile
A & y = A() + A(); // doesn't compile
A && z = A() + A(); // compiles
Why? What's going on?
A x = ... - we can't because constructors and assignment is private.
A & y = ... - we can't because we're returning a value, not a reference to a value who's scope is greater or equal to our current scope.
A && z = ... - we can because we're able to refer to xvalues. As consequence of this assignment the lifetime of the temporary value is extended to this capturing lvalue because it in effect has become an lvalue reference. Sound familiar? It's explicit elision if I were to call it anything. This is more apparent when you consider this syntax must involve a new value and must involve assigning that value to a reference.
In all three cases when all constructors and assignment is made public, there is always only three objects constructed, with the address of res always matching the variable storing the result. (on my compiler anyway, optimizations disabled, -std=gnu++11, g++ 4.9.3).
Which means the differences really do come down to just the storage duration of function arguments themselves. Elision and move operations cannot happen on anything but pure expressions, expiring values, or explicit targeting of the "expiring values" reference overload Type&&.
Re-examining f and g
I've annotated the situation in both functions to get things rolling, a shortlist of assumptions the compiler would note when generating (reusable) code for each.
A f( A && a ) {
// has storage duration exceeding f's scope.
// already constructed.
return a;
// can be elided.
// must be copy-constructed, a exceeds f's scope.
}
A g( A a ) {
// has storage duration limited to this function's scope.
// was just constructed somehow, whether by elision, move or copy.
return a;
// elision may occur.
// can move-construct if can't elide.
// can copy-construct if can't move.
}
What we can say for sure about f's a is that it's expecting to capture moved or expression-type values. Because f can accept either expression-references (prvalues) or lvalue-references about to disappear (xvalues) or moved lvalue-references (converted to xvalues via std::move), and because f must be homogenous in the treatment of a for all three cases, a is seen as a reference first and foremost to an area of memory who's lifetime exists for longer than a call to f. That is, it is not possible to distinguish which of the three cases we called f with from within f, so the compiler assumes the longest storage duration it needs for any of the cases, and finds it safest not to assume anything about the storage duration of a's data.
Unlike the situation in g. Here, a - however it happens upon its value - will cease to be accessible beyond a call to g. As such returning it is tantamount to moving it, since it's seen as an xvalue in that case. We could still copy it or more probably even elide it, it can depend on which is allowed / defined for A at the time.
The issues with f
// we can't tell these apart.
// `f` when compiled cannot assume either will always happen.
// case-by-case optimizations can only happen if `f` is
// inlined into the final code and then re-arranged, or if `f`
// is made into a template to specifically behave differently
// against differing types.
A case_1() {
// prvalues
return f( A() + A() );
}
A make_case_2() {
// xvalues
A temp;
return temp;
}
A case_2 = f( make_case_2() )
A case_3(A & other) {
// lvalues
return f( std::move( other ) );
}
Because of the ambiguity of usage the compiler and standards are designed to make f usable consistently in all cases. There can be no assumptions that A&& will always be a new expression or that you will only use it with std::move for its argument etc. Once f is made external to your code, leaving only its call signature, that cannot be the excuse anymore. The function signature - which reference overload to target - is a clue to what the function should be doing with it and how much (or little) it can assume about the context.
rvalue references are not a panacea for targeting only "moved values", they can target a good deal more things and even be targeted incorrectly or unexpectedly if you assume that's all they do. A reference to anything in general should be expected to and be made to exist for longer than the reference does, with the one exception being rvalue reference variables.
rvalue reference variables are in essence, elision operators. Wherever they exist there is in-place construction going on of some description.
As regular variables, they extend the scope of any xvalue or rvalue they receive - they hold the result of the expression as it's constructed rather than by move or copy, and from thereon are equivalent to regular reference variables in usage.
As function variables they can also elide and construct objects in-place, but there is a very important difference between this:
A c = f( A() );
and this:
A && r = f( A() );
The difference is there is no guarantee that c will be move-constructed vs elided, but r definitely will be elided / constructed in-place at some point, owing to the nature of what we're binding to. For this reason we can only assign to r in situations where there will be a new temporary value created.
But why is A&&a not destroyed if it is captured?
Consider this:
void bad_free(A && a) {
A && clever = std::move( a );
// 'clever' should be the last reference to a?
}
This won't work. The reason is subtle. a's scope is longer, and rvalue reference assignments can only extend the lifetime, not control it. clever exists for less time than a, and therefore is not an xvalue itself (unless using std::move again, but then you're back to the same situation, and it continues forth etc).
lifetime extension
Remember that what makes lvalues different to rvalues is that they cannot be bound to objects that have less lifetime than themselves. All lvalue references are either the original variable or a reference that has less lifetime than the original.
rvalues allow binding to reference variables that have longer lifetime than the original value - that's half the point. Consider:
A r = f( A() ); // v1
A && s = f( A() ); // v2
What happens? In both cases f is given a temporary value that outlives the call, and a result object (because f returns by value) is constructed somehow (it will not matter as you shall see). In v1 we are constructing a new object r using the temporary result - we can do this in three ways: move, copy, elide. In v2 we are not constructing a new object, we are extending the lifetime of the result of f to the scope of s, alternatively saying the same: s is constructed in-place using f and therefore the temporary returned by f has its lifetime extended rather than being moved or copied.
The main distinction is v1 requires move and copy constructors (at least one) to be defined even if the process is elided. For v2 you are not invoking constructors and are explicitly saying you want to reference and/or extend the lifetime of a temporary value, and because you don't invoke move or copy constructors the compiler can only elide / construct in-place!
Remember that this has nothing to do with the argument given to f. It works identically with g:
A r = g( A() ); // v1
A && s = g( A() ); // v2
g will create a temporary for its argument and move-construct it using A() for both cases. It like f also constructs a temporary for its return value, but it can use an xvalue because the result is constructed using a temporary (temporary to g). Again, this will not matter because in v1 we have a new object that could be copy-constructed or move-constructed (either is required but not both) while in v2 we are demanding reference to something that's constructed but will disappear if we don't catch it.
Explicit xvalue capture
Example to show this is possible in theory (but useless):
A && x (void) {
A temp;
// return temp; // even though xvalue, can't do this
return std::move(temp);
}
A && y = x(); // y now refers to temp, which is destroyed
Which object does y refer to? We have left the compiler no choice: y must refer to the result of some function or expression, and we've given it temp which works based on type. But no move has occurred, and temp will be deallocated by the time we use it via y.
Why didn't lifetime extension kick in for temp like it did for a in g / f? Because of what we're returning: we can't specify a function to construct things in-place, we can specify a variable to be constructed in place. It also goes to show that the compiler does not look across function / call boundaries to determine lifetime, it will just look at which variables are on the calling side or local, how they're assigned to and how they're initialized if local.
If you want to clear all doubts, try passing this as an rvalue reference: std::move(*(new A)) - what should happen is that nothing should ever destroy it, because it isn't on the stack and because rvalue references do not alter the lifetime of anything but temporary objects (ie, intermediates / expressions). xvalues are candidates for move construction / move assignment and can't be elided (already constructed) but all other move / copy operations can in theory be elided on the whim of the compiler; when using rvalue references the compiler has no choice but to elide or pass on the address.

Are all temporaries rvalues in C++?

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;