I am reading through Programming: Principles and Practice in C++, 4th edition by Stroustrup and I am confused about the output of some code that is intended to illustrate the copy constructor/copy assignment/move constructor/move assignment/destructor language features. The following code is largely pulled from the book and implements a simple type X
so that the above operators (as well as the constructor) output explicit information when they are called.
#include <iostream>
#include <string>
#include <vector>
using std::cout;
using std::string;
using std::vector;
struct X {
int val;
void out(const string& s, int nv) {
cout << this << "->" << s << ":" << val << "(" << nv << ")\n";
}
X() { out("X()",0); val=0; }
explicit X(int x) { out("X(int)",x); val=x; }
X(const X& x) { out("X(X&)", x.val); val=x.val; }
X& operator=(const X& x) {
out("X copy assignment", 0);
val=x.val;
return *this; }
X(X&& x) {
out("X(X&&)",x.val);
val = x.val;
x.val = 0;
}
X& operator=(X&& x) {
out("X move assignment", x.val);
val = x.val;
x.val = 0;
return *this;
}
~X() { out("~X()", 0); }
};
X copy(X a) {
cout << "copy()\n";
return a;
}
int main() {
X loc {4};
X loc2 {12};
loc2 = copy(loc);
cout << "Done\n";
}
The output that I am seeing when I run this program is the following:
0x16d1370bc->X(int):1(4)
0x16d1370b8->X(int):-1834548312(12)
0x16d1370b0->X(X&):1829990608(4)
copy()
0x16d1370b4->X(X&&):1(4)
0x16d1370b8->X move assignment:12(4)
0x16d1370b4->~X():0(0)
0x16d1370b0->~X():0(0)
Done
0x16d1370b8->~X():4(0)
0x16d1370bc->~X():4(0)
I understand the first four lines of this output -- they are from the constructors of the two local variables loc
and loc2
, the copy constructor from the call-by-value of copy
, and the print from the copy
function body. I am confused by the next line -- namely, the call to the move constructor. I was expecting to just see a call to the move assignment operator. Why is the move constructor called here (I did not think there was any object being constructed?)? What is it being called on? In particular, I do not see a construction of an object with the memory address that is printed by the move constructor, so I am confused where that object came from.
Thank you for your help, I apologize that I am new to C++. If it is relevant, I am compiling this program with g++ -Wall -std=c++20
. the output has persisted with different compilation optimization levels.
As your output suggests, the expression loc2 = copy(loc)
involves a copy, a move, and an assignment:
loc
is used to copy-construct the a
parameter of copy
a
in copy
is used to move-construct copy
's return value.copy
is passed as the x
parameter to X
's move-assignment operatorIf I had to guess, you're not thinking of copy
's return value as a distinct object. In many cases, that's accurate, since copy elision will elide that particular copy. That optimization is mandatory if you return a prvalue (i.e. return X{42};
) and are using C++17 or later, but it is optional when returning a named object, since it's not possible in 100% of cases.