调用转换函数后是否调用了移动构造函数?

Bar*_*rry 17 c++ initialization language-lawyer c++11 c++17

考虑这个例子:

struct T { };

struct S {
    operator T();
};

S s;
T t = s;
Run Code Online (Sandbox Code Playgroud)

[dcl.init]会将我们带到[over.match.copy],它会找到转换函数operator T().但是我们在那时完成了,还是我们必须调用T(T&& rhs),绑定rhsoperator T()via [dcl.init.ref] 的返回?关于C++ 11和C++ 1z之间这个问题的答案有什么不同吗?

T.C*_*.C. 15

这属于[dcl.init] /17.6.3,这非常清楚重载决策选择转换函数后会发生什么:

选择的函数以初始化表达式作为参数调用; 如果函数是构造函数,则调用是目标类型的cv-nonqualified版本的prvalue,其结果对象由构造函数初始化.该调用用于根据上述规则直接初始化作为复制初始化目标的对象.

在你的情况下,这反过来recurses[dcl.init] /17.6.1:

如果初始化表达式是prvalue且源类型的cv-nonqualified版本与目标类相同,则初始化表达式用于初始化目标对象.


在C++ 11中,第二步确实调用了一个移动构造函数,因为它没有与C++ 17的17.6.1相对应的项目符号.而是再次进行直接初始化/重载分辨率舞蹈:

如果初始化是直接初始化,则考虑构造函数.枚举适用的构造函数([over.match.ctor]),并通过重载决策([over.match])选择最佳构造函数.调用所选的构造函数来初始化对象,初始化表达式或表达式列表作为其参数.如果没有构造函数适用,或者重载决策是不明确的,则初始化是错误的.

这一举动可以(并且在实践中)将被省略; 见[class.copy]/31.


实际上更有趣的案例

T t(s);
Run Code Online (Sandbox Code Playgroud)

在C++ 17下,实际上需要调用一个移动构造函数,因为它使用直接初始化规则并对T构造函数执行重载决策.它选择了T移动构造函数并调用它进行初始化t,转换为实现为临时sTprvalue并绑定到移动构造函数的参数.在这个过程中根本无法访问17.6.1项目符号,并且在C中删除了C++ 11的[class.copy]/31(现在[class.copy.elision]/1)中允许省略的子弹++ 17.

这很可能是一个缺陷.

  • 非常感谢你.我,我很期待提出简化初始化的论文...... (3认同)