c++referenceaggregate-initialization

Is this reference-initialization or aggregate-initialization?


I have the following code snippet:

struct A {};
struct B : A{};

B b{ A() };

Does the implicitly-declared copy constructor B::B(const B&) is used here so that the reference (const B&) is bound to B subobject of initialzier expression A()? and why no?

If this is an aggregate initialization, this means that the ctor B::B(const B&) is never called, so why when I explicitly deleted it, the program is ill-formed?


Solution

  • From C++17 onwards, B b{ A() }; is aggregate initialization.

    Prior C++17

    Prior to C++17, the class-type B is not an aggregate. So B b{ A() }; cannot be aggregate initialization. In particular, B b{ A() }; is direct initialization:

    The effects of list-initialization of an object of type T are:

    • Otherwise, the constructors of T are considered, in two phases:

      • If the previous stage does not produce a match, all constructors of T participate in overload resolution against the set of arguments that consists of the elements of the braced-init-list, with the restriction that only non-narrowing conversions are allowed. If this stage produces an explicit constructor as the best match for a copy-list-initialization, compilation fails (note, in simple copy-initialization, explicit constructors are not considered at all).

    (emphasis mine)

    This means that all the implicitly declared ctors of B are considered but none of them can be used here. For example, the copy/move ctor cannot be used because they have a parameter of type const B& or B&& respectively and neither of those can be bound to the passed A object as there is no way to implicitly convert an A to B in your example. Thus the copy/move ctor cannot be used.

    Similarly, the default ctor B::B() cannot be used because it has no parameter but we're passing an A.

    Thus this fails prior to c++17 with the error:

    <source>:15:4: error: no matching constructor for initialization of 'B'
     B b{ A() };
       ^~~~~~~~
    <source>:11:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'A' to 'const B' for 1st argument
    struct B : A{
           ^
    <source>:11:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'A' to 'B' for 1st argument
    struct B : A{
           ^
    <source>:11:8: note: candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided
    1 error generated.
    

    C++17

    From C++17 onwards, B is an aggregate because it has public base class. This means, now B b{ A() }; is an aggregate initialization.

    The effects of aggregate initialization are:

    • Otherwise, if the initializer list is non-empty, the explicitly initialized elements of the aggregate are the first n elements of the aggregate, where n is the number of elements in the initializer list.