gcc和clang都忽略了下面代码段中对move构造函数的调用.它是否正确?

Bel*_*loc 6 c++ initialization language-lawyer copy-elision c++11

在下面的代码中s,类的对象S用于使用D直接初始化初始化类的对象D d(s);.转换函数S :: operator 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);
}
Run Code Online (Sandbox Code Playgroud)

我基于以下理由对这个省略的正确性提出异议:

  1. 这个案例在§8.5/ 16(N3337)的第一个子项目中有所涉及,该部分没有提及elision.

    如果初始化是直接初始化,或者它是复制初始化,其中源类型的cv-nonqualified版本与目标类相同的类或派生类,则考虑构造函数.列举了适用的构造函数(13.3.1.3),并通过重载解析(13.3)选择最佳构造函数.调用所选的构造函数来初始化对象,初始化表达式或表达式列表作为其参数.如果没有构造函数适用,或者重载决策是不明确的,则初始化是错误的.

  2. 请注意,下一个子项目明确提到了省略的可能性.
  3. 对move构造函数的调用是显式的.怎么可以省略?

Seb*_*edl 8

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

复制/移动省略规则在12.8/31中规定.您的代码中有两个要删除的复制/移动操作.

第一个很简单:在内部operator D,返回表达式中构造的临时值被移动到表示函数返回值的临时值.子弹3允许省略此举.

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

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