lo tolmencre lo tolmencre - 3 months ago 8
C++ Question

Missing constructor call

I was confused why my program behaved in an unexpected way. Then I wrote this simplified version of it and discovered that there is a constructor call "missing".

template <class T>
class A
{
public:

A () {std::cout << "default" << "\n";} // default ctor

A (const A& src) // copy ctor
{
std::cout << "copy" << "\n";
}

friend A<T> operator<<(A& a, unsigned i)
{
std::cout << "x1" << "\n";
A tmp;
std::cout << "x2" << "\n";
return tmp;
}
};


int main()
{
A<int> a1;
A<int> a2(a1 << 2);
}


Output

default
x1
default
x2


What I had expected was

default
x1
default
x2
copy


as the r-value returned by
a1 << 2
would be passed into the
const A&
parameter of the copy ctor. But that is not what happens. And if not that then at least I would expect

default
x1
default
x2
default


because I would have thought that the constructor for
a2
would need to be called.

What is going on here?

Answer

This is because of copy elision. More specifically, Named Return Value Optimization, or NRVO.

NRVO happens when a local (to the function) variable with automatic storage duration is returned by value from a function, and that value is assigned to a variable:

friend A<T> operator<<(A& a, unsigned i)
{
    //...
    A tmp; //automatic storage variable 
    //...
    return tmp; //returned by value
}

A<int> a2(a1 << 2); //Assigned to a2

The compiler is allowed to elide the copy of tmp to a2. This basically means that when the function ends, the memory of tmp is not deallocated! Then, it just assigns a2 to that memory location, which basically results in a "copy" of tmp.

This happens if even the copy/move constructors and destructor have side-effects, like outputting some value. Due note however that this is implementation defined, some compiler might as well output the second and/or the third case.

For other forms of copy elision, refer to those answers.

Comments