调用转换函数后是否调用移动构造函数?
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.
这很可能是缺陷。
考虑这个例子:
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.
这很可能是缺陷。