GCC 和 Clang 都省略了对以下代码片段中移动构造函数的调用.这是对的吗



在下面的代码中,类 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);
}

我对这种省略的正确性提出异议,理由如下:

  1. 这种情况在§8.5/16(N3337(的第一个子项目符号中进行了介绍,该子项目符号对省略号保持沉默。

    如果初始化是直接初始化,或者如果是 复制初始化,其中源的 cv 非限定版本 类型与 目标,考虑构造函数。适用的构造函数 枚举 (13.3.1.3(,并通过重载选择最佳 分辨率 (13.3(。调用如此选择的构造函数来初始化 对象,其中初始值设定项表达式或表达式列表为其 参数。如果没有构造函数应用,或者重载解析为 模棱两可,初始化格式不正确。

  2. 请注意,下一个子项目符号点明确提到了省略的可能性。
  3. 对移动构造函数的调用是显式的。如何省略它?

C++标准喜欢为在一个地方、一个完全不同的地方定义的规则创建例外。

复制/移动省略规则在 12.8/31 中指定。代码中需要消除两个复制/移动操作。

第一个很简单:在 operator D 中,返回表达式中构造的临时被移动到表示函数返回值的临时。项目符号 3 允许省略这一举动。

第二个是将临时函数返回值移动到d对象。同样,子弹 3 允许省略。

  • 当尚未绑定到引用 (12.2( 的临时类对象将被复制/移动到具有相同 CV-UNQUALIFIED 类型的类对象时,可以通过将临时对象直接构造到省略的复制/移动的目标中来省略复制/移动操作

最新更新