Why is the copy constructor called twice in this code snippet?

First copy is in return of createX

X createX() {
    X x;
    std::cout << "created x on the stack" << std::endl;
    return x; // First copy
}

Second one is to create x2 from the temporary return by createX.

X x2 = createX(); // Second copy

Notice that in C++17, second copy is forced to be elided.


What you have to remember here is that the return value of a function is a distinct object. When you do

return x;

you copy initialize the return value object with x. This is the first copy constructor call you see. Then

X x2 = createX();

uses the returned object to copy initialize x2 so that is the second copy you see.


One thing to note is that

return x;

will try to move x into the return object if it can. Had you made a move constructor you would have seen this called. The reason for this is that since local objects go out of scope at the end of the function, the compiler treats the object as an rvalue and only if that does not find a valid overload does it fall back to returning it as an lvalue.