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

Is the move constructor called after invoking a conversion function?

考虑这个例子:

struct T { };

struct S {
    operator T();
};

S s;
T t = s;

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

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

The function selected is called with the initializer expression as its argument; if the function is a constructor, the call is a prvalue of the cv-unqualified version of the destination type whose result object is initialized by the constructor. The call is used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization.

在你的例子中,这又递归到 [dcl.init]/17.6.1:

If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object.


在 C++11 中,第二步调用移动构造函数,因为它没有对应于 C++17 的 17.6.1 的项目符号。相反 you do the direct-initialization/overload resolution dance again:

If the initialization is direct-initialization, [...], constructors are considered. The applicable constructors are enumerated ([over.match.ctor]), and the best one is chosen through overload resolution ([over.match]). The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.

此步棋可以(实际上将会)被省略;参见 [class.copy]/31


比较有意思的案例其实是

T t(s);

根据 C++17 的措辞,实际上 需要 来调用移动构造函数,因为它使用直接初始化规则并对 T 进行重载解析的构造函数。选择 T 的移动构造函数并调用它来初始化 t,将 s 转换为 T 纯右值,该纯右值具体化为临时值并绑定到移动的参数构造函数。 17.6.1 项目符号在此过程中根本无法访问,并且在 C++ 中删除了 C++11 的 [class.copy]/31(现在 [class.copy.elision]/1)中允许省略的项目符号17.

这很可能是缺陷。