Question

For the following code:

struct A {
    explicit A(int) {}
};

const A& a(1);    // error on g++/clang++/vc++
const A& b = 1;   // error on g++/clang++/vc++
const A& c{1};    // ok on g++/clang++, error on vc++
const A& d = {1}; // error on g++/clang++/vc++

Which one(s) of the 4 initialization is(are) legal?

If we ignore vc++ first, it seems that the difference between direct-init and copy-init is not behaving consistently here. If the third one is well-formed because it's direct-init, why does the first one which is also direct-init fail to compile? What's the logic behind this? Or it's just a bug for g++/clang++ and vc++ handles it correctly?

Was it helpful?

Solution

If you're using braced-init-lists and the destination type of an initialization is a reference:

[dcl.init.list]/3 (from n3690)

  • Otherwise, if the initializer list has a single element of type E and either T is not a reference type or its referenced type is reference-related to E, the object or reference is initialized from that element; if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed.

  • Otherwise, if T is a reference type, a prvalue temporary of the type referenced by T is copy-list-initialized or direct-list-initialized, depending on the kind of initialization for the reference, and the reference is bound to that temporary. [Note: As usual, the binding will fail and the program is ill-formed if the reference type is an lvalue reference to a non-const type. — end note]

  • Otherwise, if the initializer list has no elements, the object is value-initialized.

For the two examples const A& c{1}; and const A& d = {1};, the second bullet of the quotation above applies. The first one direct-list-initializes a const A, the second one copy-list-initializes a const A. Copy-initialization selecting an explicit constructor is ill-formed, see [over.match.list]/1.


If you're not using braced-init-lists, there's no difference between copy-initialization and direct-initialization as far as I can tell. The last bullet of [dcl.init.ref]/5 applies in all cases:

  • Otherwise, a temporary of type “cv1 T1” is created and initialized from the initializer expression using the rules for a non-reference copy-initialization (8.5). The reference is then bound to the temporary.

Copy-initialization cannot select an explicit ctor, see [over.match.copy]/1 (it's not viable).


Conclusion:

const A& c{1};

is legal. The other are not, because they either use copy-initialization or copy-list-initialization and the only viable / selected ctor is explicit.

OTHER TIPS

Your struct can only be created by an explicit call to it's constructor. No implicit conversion is allowed.

A const reference must point to an existing object of that type on construction.

None of your lines create an object of type A by calling it's explicit constructor. So I don't see why any of those lines should properly initialize a const reference.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top