在下面的代码中,类 S
的对象s
用于使用直接初始化D d(s);
初始化类 D
的对象。转换函数 S::运算符 D(( 用于将对象s
转换为类型为 D
的临时对象。然后,gcc 和 clang 都省略了对 move 构造函数 D(&&)
的显式调用,将这个临时对象移动到 d
中。请参阅实时示例。
#include <iostream>
struct D;
struct S{ operator D(); };
struct D{
D(){}
D(D&&) { std::cout << "move constructor" << 'n'; }
};
S::operator D() { std::cout << "conversion function" << 'n'; return D(); }
int main()
{
S s;
D d(s);
}
我对这种省略的正确性提出异议,理由如下:
- 这种情况在§8.5/16(N3337(的第一个子项目符号中进行了介绍,该子项目符号对省略号保持沉默。
如果初始化是直接初始化,或者如果是 复制初始化,其中源的 cv 非限定版本 类型与 目标,考虑构造函数。适用的构造函数 枚举 (13.3.1.3(,并通过重载选择最佳 分辨率 (13.3(。调用如此选择的构造函数来初始化 对象,其中初始值设定项表达式或表达式列表为其 参数。如果没有构造函数应用,或者重载解析为 模棱两可,初始化格式不正确。
- 请注意,下一个子项目符号点明确提到了省略的可能性。
- 对移动构造函数的调用是显式的。如何省略它?
C++标准喜欢为在一个地方、一个完全不同的地方定义的规则创建例外。
复制/移动省略规则在 12.8/31 中指定。代码中需要消除两个复制/移动操作。
第一个很简单:在 operator D
中,返回表达式中构造的临时被移动到表示函数返回值的临时。项目符号 3 允许省略这一举动。
第二个是将临时函数返回值移动到d
对象。同样,子弹 3 允许省略。
- 当尚未绑定到引用 (12.2( 的临时类对象将被复制/移动到具有相同 CV-UNQUALIFIED 类型的类对象时,可以通过将临时对象直接构造到省略的复制/移动的目标中来省略复制/移动操作