Is this reference-initialization or aggregate-initialization?

114 Views Asked by At

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?

1

There are 1 best solutions below

7
user12002570 On BEST ANSWER

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.