operator+ 的规范实现涉及额外的移动构造函数

Canonical implementation of operator+ involves additional move constructor

的启发,我根据 operator+= 比较了二进制 operator+ 的两个不同版本的实现。考虑我们在 class X 的定义中。

版本 1

friend X operator+(X lhs, const X& rhs)   
{
   lhs += rhs;  
   return lhs; 
}

版本 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);
}

其中,在这两种情况下,operator+= 定义如下:

X& operator+=(const X& rhs)    
{                             
  ... // whatever to add contents of X
  return *this;   
}

现在,我只是 运行 以下代码并跟踪了 copy/move 构造函数的调用:

X a, b, c;
X d = a + b + c;

对于第一个 "canonical" 版本,有 1 次复制 + 2 次 move 构造函数调用,而对于第二个版本只有 1 个副本 + 1 个 move 构造函数调用(使用 GCC 10 和 -O3 测试)。

问题:在第一种情况下,是什么阻碍了对附加移动构造函数调用的省略?

现场演示:https://godbolt.org/z/GWEnHJ


补充观察:在现场演示中,class有一些内容(整数成员变量),移动构造函数调用是not/arefirst/second版本内联,分别。此外,对于第二个版本,最终结果 6 在编译时计算并硬编码到程序集中(当传递给 operator<< 时),而对于第一个版本,它是从内存中读取的。 一般来说,第二个版本似乎(相对)更有效。但这可能是由涉及的那些 cout 消息引起的。没有它们,汇编输出完全一样。

What hinders the elision of that additional move constructor call in the first case?

缺陷报告 DR1148 已被接受并包含在 C++11 中。

简而言之,它说(强调我的):

It is unclear whether copy elision is permitted when returning a parameter of class type. If not, it should still be possible to move, rather than copy, the return value.

Suggested resolution: Amend paragraph 34 to explicitly exclude function parameters from copy elision. Amend paragraph 35 to include function parameters as eligible for move-construction.

结果可以在 [class.copy.elision]/1.1强调我的

in a return statement in a function with a class return type, when the expression is the name of a non-volatile object with automatic storage duration (other than a function parameter or a variable introduced by the exception-declaration of a handler ([except.handle])) with the same type (ignoring cv-qualification) as the function return type, the copy/move operation can be omitted by constructing the object directly into the function call's return object