From cppreference,
When copy elision occurs, the implementation treats the source and target of the omitted copy/move (since C++11) operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization (except that, if the parameter of the selected constructor is an rvalue reference to object type, the destruction occurs when the target would have been destroyed) (since C++17).
For a simple case like A a = returnA();
, I can understand that the object is not destroyed in returnA()
and instead the destruction occurs as in the case A a;
which is the later time.
I can't think of a case which the opposite happens such that the source of the copy/move operation is destroyed first. Also I would like an example of the added statement since C++17 (exception when parameter of selected constructor is an rvalue reference to object type)
The symmetric case where the source outlives the target is when the prvalue is a parameter:
struct A {
static int *data;
A() {if(!refs++) data=new int(42);}
A(const A&) {++refs;} // not movable
~A() {if(!--refs) delete data;}
private:
static int refs;
};
int A::refs,*A::data;
int* f(A) {return A::data;}
A returnA();
int returnInt() {return *f(returnA());} // ok
Because the result of returnA()
is a temporary, its lifetime extends to the end of the return
statement’s full-expression. The implementation may identify it with f
’s parameter, but may not destroy it when f
returns, so the dereference in returnInt
is valid. (Note that parameters may survive that long anyway.)
The adjustment in C++17 (along with such elision being guaranteed) is that if you (would) move the prvalue, it may be destroyed when the parameter is (since you shouldn’t be relying on its contents anyway). If that’s when f
returns, the (ill-advised) code above becomes invalid if A
is made movable.