Title 670. Copy initialization via derived-to-base conversion in the second step
Status open Section 8.6 [dcl.init]
Submitter Jason Merrill

Created on 2007-12-20.00:00:00 by admin, last changed by admin.

msg1572 (view) Date: 2007-12-20.00:00:00

In this example:

    struct A {};

    struct B: A {

    void foo(B);

    void bar() {

we are copy-initializing a B from 0. So by [over.match.copy] we consider all the converting constructors of B, and choose B(int) to create a B. Then, by 8.6 [dcl.init] paragraph 15, we direct-initialize the parameter from that temporary B. By [over.match.ctor] we consider all constructors. The copy constructor cannot be called with a temporary, but B(A) is callable.

As far as I can tell, the Standard says that this example is well-formed, and calls B(A). EDG and G++ have rejected this example with a message about the copy constructor not being callable, but I have been unsuccessful in finding anything in the Standard that says that we only consider the copy constructor in the second step of copy-initialization. I wouldn't mind such a rule, but it doesn't seem to be there. And implementing issue 391 causes G++ to start accepting the example.

This question came up before in a GCC bug report; in the discussion of that bug Nathan Sidwell said that some EDG folks explained to him why the testcase is ill-formed, but unfortunately didn't provide that explanation in the bug report.

I think the resolution of issue 391 makes this example well-formed; it was previously ill-formed because in order to bind the temporary B(0) to the argument of A(const A&) we needed to make another temporary B, and that's what made the example ill-formed. If we want this example to stay ill-formed, we need to change something else.

Steve Adamczyk:

I tracked down my response to Nathan at the time, and it related to my paper N1232 (on the auto_ptr problem). The change that came out of that paper is in [] paragraph 4:

However, when considering the argument of a user-defined conversion function that is a candidate by [over.match.ctor] when invoked for the copying of the temporary in the second step of a class copy-initialization, or by [over.match.copy], [over.match.conv], or [over.match.ref] in all cases, only standard conversion sequences and ellipsis conversion sequences are allowed.

This is intended to prevent use of more than one implicit user- defined conversion in an initialization.

I told Nathan B(A) can't be called because its argument would require yet another user-defined conversion, but I was wrong. I saw the conversion from B to A and immediately thought “user-defined,” but in fact because B is a derived class of A the conversion according to [] paragraph 6 is a derived-to-base Conversion (even though it will be implemented by calling a copy constructor).

So I agree with you: with the analysis above and the change for issue 391 this example is well-formed. We should discuss whether we want to make a change to keep it ill-formed.

Date User Action Args
2017-02-06 00:00:00adminsetsection: 8.5 [dcl.init] -> 8.6 [dcl.init]
2007-12-20 00:00:00admincreate