Is the move constructor called after invoking a conversion function?

我与影子孤独终老i 提交于 2019-12-10 01:21:34

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!