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

Dan*_*ica 5 c++ operator-overloading pass-by-reference pass-by-value language-lawyer

通过启发这个问题,我比较了两个不同版本的二进制的实施operator+来讲operator+=。考虑我们在 class 的定义中X

版本 1

friend X operator+(X lhs, const X& rhs)   
{
   lhs += rhs;  
   return lhs; 
}
Run Code Online (Sandbox Code Playgroud)

版本 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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

现在,我只运行以下代码并跟踪复制/移动构造函数的调用:

X a, b, c;
X d = a + b + c;
Run Code Online (Sandbox Code Playgroud)

在第一个“规范”版本中,有 1 个副本 + 2 个移动构造函数调用,而在第二个版本中,只有 1 个副本 + 1 个移动构造函数调用(使用 GCC 10 和 测试-O3)。

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

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


附加观察:在现场演示,其中所述类有一些内容(整数成员变量)时,移动电话的构造并不/被内联与所述第一/第二版,分别。此外,对于第二个版本,最终结果 6 在编译时计算并硬编码到程序集中(当传递给 时operator<<),而对于第一个版本,它是从内存中读取的。通常,第二个版本似乎(相对)效率更高。但这很可能是由这些cout消息引起的。没有它们,装配输出完全相同。

Ted*_*gmo 2

在第一种情况下,是什么阻碍了额外的移动构造函数调用的省略?

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

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

目前尚不清楚返回类类型的参数时是否允许复制省略。如果没有,应该仍然可以移动而不是复制返回值。

建议的解决方案:修改第 34 段以明确从复制省略中排除函数参数。修改第 35 段,将函数参数纳入移动构造的资格。

结果可以在 [class.copy.elision]/1.1中找到(重点是我的

return具有类返回类型的函数的语句中,当表达式具有自动存储持续时间的非易失性对象的名称(函数参数或由处理程序异常声明引入的变量( )除外)时,与函数返回类型相同的类型(忽略 cv 限定),可以通过将对象直接构造到函数调用的返回对象中来省略复制/移动操作[except.handle]