问题
Consider this example:
struct T { };
struct S {
operator T();
};
S s;
T t = s;
[dcl.init] will take us to [over.match.copy] which will find the conversion function operator T()
. But are we done at that point, or do we have to invoke T(T&& rhs)
, binding rhs
to the return of operator T()
via [dcl.init.ref]? Are there any differences with regards to the answer to this question between C++11 and C++1z?
回答1:
This falls under [dcl.init]/17.6.3, which is pretty clear about what happens after overload resolution selects the conversion function:
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.
In your case this in turn recurses into [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.
In C++11 the second step does invoke a move constructor, since it doesn't have the bullet corresponding to C++17's 17.6.1. Instead 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.
This move can (and in practice will) be elided; see [class.copy]/31.
The more interesting case is actually
T t(s);
which under the C++17 wording is actually required to call a move constructor, because it uses the direct-initialization rule and does overload resolution on T
's constructors. That selects T
's move constructor and calls it to initialize t
, converting s
to a T
prvalue that is materialized into a temporary and bound to the parameter of the move constructor. The 17.6.1 bullet is simply not reachable in the process, and the bullet in C++11's [class.copy]/31 (now [class.copy.elision]/1) that permitted elision in this scenario was removed in C++17.
This is most likely a defect.
来源:https://stackoverflow.com/questions/44311306/is-the-move-constructor-called-after-invoking-a-conversion-function