Does C++17 forbid copy elision in a case where C++14 allowed it?

China☆狼群 提交于 2019-11-29 13:34:45

As T.C. points out, this is in a similar vein to CWG 2327:

Consider an example like:

struct Cat {};
struct Dog { operator Cat(); };

Dog d;
Cat c(d);

This goes to 11.6 [dcl.init] bullet 17.6.2:

Otherwise, if the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated (16.3.1.3 [over.match.ctor]), and the best one is chosen through overload resolution (16.3 [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.

Overload resolution selects the move constructor of Cat. Initializing the Cat&& parameter of the constructor results in a temporary, per 11.6.3 [dcl.init.ref] bullet 5.2.1.2. This precludes the possitiblity of copy elision for this case.

This seems to be an oversight in the wording change for guaranteed copy elision. We should presumably be simultaneously considering both constructors and conversion functions in this case, as we would for copy-initialization, but we'll need to make sure that doesn't introduce any novel problems or ambiguities.

What makes this the same underlying problem is that we have an initializer (in OP, {}, in this example, d) that's the wrong type - we need to convert it to the right type (X or Cat), but to figure out how to do that, we need to perform overload resolution. This already gets us to the move constructor - where we're binding that rvalue reference parameter to a new object that we just created to make this happen. At this point, it's too late to elide the move. We're already there. We can't... back up, ctrl-z, abort abort, okay start over.

As I mentioned in the comments, I'm not sure this was different in C++14 either. In order to evaluate X x({}), we have to construct an X that we're binding to the rvalue reference parameter of the move constructor - we can't elide the move at that point, the reference binding happens before we even know we're doing a move.

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