运算符+ 的规范实现涉及额外的移动构造函数
Canonical implementation of operator+ involves additional move constructor
受这个问题的启发,我比较了二进制operator+
实现的两个不同版本operator+=
。考虑我们在类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;
}
现在,我只运行以下代码并跟踪复制/移动构造函数的调用:
X a, b, c;
X d = a + b + c;
对于第一个"规范">版本,有 1 个副本 + 2 个移动构造函数调用,而在第二个版本中,只有 1 个副本 +1 个移动构造函数调用(使用 GCC 10 和-O3
进行测试(。
问题:在第一种情况下,是什么阻碍了该附加移动构造函数调用的省略?
现场演示:https://godbolt.org/z/GWEnHJ
其他观察:在实时演示中,类具有一些内容(整数成员变量(,移动构造函数调用没有/分别与第一个/第二个版本内联。此外,对于第二个版本,最终结果 6 在编译时计算并硬编码到程序集中(当传递给operator<<
时(,而在第一个版本中,它是从内存中读取的。一般来说,第二个版本似乎(相对(更有效率。但这很可能是由所涉及的cout
消息引起的。没有它们,程序集输出完全相同。
在第一种情况下,是什么阻碍了该额外移动构造函数调用的省略?
缺陷报告 DR1148 被接受并包含在 C++11 中。
简而言之,它说(强调我的(:
目前尚不清楚在返回类类型的参数时是否允许复制省略。如果没有,应该仍然可以移动而不是复制返回值。
建议的解决方案:修改第34段,明确排除函数参数。修正第35段,列入符合移动结构条件的功能参数。
结果可以在 [class.copy.elision]/1.1 (强调我的(
在具有类返回类型的函数的
return
语句中,当表达式是具有自动存储持续时间的非易失性对象的名称(函数参数或处理程序的异常声明引入的变量除外(时([except.handle]
(具有与函数返回类型相同类型(忽略 cv-quality(的类型,可以通过将对象直接构造到函数调用中来省略复制/移动操作。返回对象
- 为什么不调用移动构造函数?(默认情况下只有构造器,没有别的)
- std::vector::p ush_back() 不会在 MSVC 上编译具有已删除移动构造函数的对象
- 仅包含可移动 std::map 的类的移动构造函数不起作用
- 为什么调用复制构造函数而不是移动构造函数?
- 基类中的默认析构函数禁用子类中的移动构造函数(如果有成员)
- 从具有按值捕获的 lambda 移动构造 std::函数时,移动构造函数调用两次
- 移动构造函数和右值引用
- 为什么 std::memmove 中联合的默认非平凡移动构造函数C++?
- 具有专用化的模板类中的可靠条件复制和移动构造函数
- C++:为什么不调用移动构造函数?
- 移动构造函数永远不会被调用
- C++:关于使用 Stroustrup 示例移动构造函数/赋值的问题
- 运算符+ 的规范实现涉及额外的移动构造函数
- C ++为什么在移动构造函数中需要移动/前进
- 为什么在删除"移动构造函数"时使用"复制构造函数"?
- 为什么这里不调用移动构造函数?
- 隐式移动构造函数
- 如何为具有私有成员的派生类实现移动构造函数
- 为什么不调用移动构造函数
- 是否可以避免在以下代码中复制/移动构造函数的需要?