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?
From C++17 onwards, B b{ A() };
is aggregate initialization.
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.
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.