问题
Motivated by this question, I compared two different versions of an implementation of a binary operator+
in terms of operator+=
. Consider we are inside the definition of class X
.
Version 1
friend X operator+(X lhs, const X& rhs)
{
lhs += rhs;
return lhs;
}
Version 2
friend X operator+(const X& lhs, const X& rhs)
{
X temp(lhs);
temp += rhs;
return temp;
}
friend X operator+(X&& lhs, const X& rhs)
{
lhs += rhs;
return std::move(lhs);
}
Where, in both cases, operator+=
is defined as follows:
X& operator+=(const X& rhs)
{
... // whatever to add contents of X
return *this;
}
Now, I just run the following code and tracked calls of copy/move constructors:
X a, b, c;
X d = a + b + c;
With the first "canonical" version, there were 1 copy + 2 move constructor calls, while with the second version there were just 1 copy + 1 move constructor calls (tested with GCC 10 and -O3
).
Question: What hinders the elision of that additional move constructor call in the first case?
Live demo: https://godbolt.org/z/GWEnHJ
Additional observation: In the live demo, where the class has some contents (integer member variable), the move constructor calls are not/are inlined with the first/second version, respectively. Also, with the second version, the final result 6 is calculated at compile time and hard-coded into the assembly (when passed to operator<<
), while with the first version, it is read from memory. Generally, the second version seems to be (relatively) much more efficient. But this was likely caused by those cout
messages involved. Without them, the assembly output was exactly the same.
来源:https://stackoverflow.com/questions/62179683/canonical-implementation-of-operator-involves-additional-move-constructor