3

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?

mada
  • 1,646
  • 1
  • 15
  • This(`B b{ A() }`) is *direct initialization* and the program will not compile prior to C++17. See [demo](https://onlinegdb.com/oSlIp7aPLy). It will only compile with C++17 or later. – Jason Aug 25 '22 at 17:04
  • 1
    @JasonLiam - It compiles with g++11.2.0 with flag -std=c++20 – mada Aug 25 '22 at 17:04
  • Compiler version doesn't matter. The code won't compile prior to C++17. See [demo](https://onlinegdb.com/oSlIp7aPLy) It will only compile with C++17 or later. – Jason Aug 25 '22 at 17:06
  • @JasonLiam "_This(B b{ A() }) is direct initialization_". therefore? – mada Aug 25 '22 at 17:13
  • Reference initialization requires a reference to initialize. – user4581301 Aug 25 '22 at 17:14
  • 1
    I think this is an aggregate initialization because the class `B` isn't _explicitly_ declare a constructor matches the given argument-list – mada Aug 25 '22 at 17:16
  • @user4581301 - the reference he maybe meant is the reference parameter of the implicitly-declared `B::B(const B&)` – mada Aug 25 '22 at 17:18
  • @H.K. `B` is an aggregate from C++17 onwards but before C++17 `B` was not an aggregate. – Jason Aug 25 '22 at 17:21
  • @JasonLiam - "_but before C++17 B was not an aggregate._" what it was be? – mada Aug 25 '22 at 17:27
  • @H.K. What do you mean? `B` was not aggregate before C++17 because it has a base class. – Jason Aug 25 '22 at 17:28
  • Humans tend to name things, so maybe there is a name more meaningful than non-aggregate, but seeing as aggregate is a modifier for a class, a non-aggregate class is probably just a class. Similar to not having a term other than non-`static` member or a class member that isn't `static`. – user4581301 Aug 25 '22 at 17:29
  • @JasonLiam Before C++17, B is what? – mada Aug 25 '22 at 17:29
  • @H.K. A `class type` that is not an aggregate. – Jason Aug 25 '22 at 17:30
  • @JasonLiam So the concept of a "class aggregate" is appeared only in c++17 onwards? – mada Aug 25 '22 at 17:32
  • 1
    @H.K. No, the concept of class aggregate does not appear only in C++17. It was there before c++17 also. Note carefully `B` has base class. Also refer to a [good c++ book](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) – Jason Aug 25 '22 at 17:33
  • @JasonLiam - Woow! Awesome reference - Really thanks for this. – mada Aug 25 '22 at 17:35
  • The more descriptive power a language offers, the more complicated the language will be. C++ offers up **a lot** of power – user4581301 Aug 25 '22 at 17:36

1 Answers1

2

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.
Jason
  • 36,170
  • 5
  • 26
  • 60
  • What's the difference between `B b{ A() }` and `A a{ B() }`? ` – mada Aug 25 '22 at 17:59
  • @H.K. In this(`A a{ B() }`) case, the parameter of the copy constructor `A::A(const A&)` is of type `const A&` which can be bound to a `B` object. Feel free to create/ask a separate new question for your follow up question where i can explain in more detail what happens in `A a{ B() }` as there is word-limit in comments section and also comment often get deleted for different reasons. – Jason Aug 25 '22 at 18:14
  • _" In this(A a{ B() }) case, the parameter of the copy constructor A::A(const A&) is of type const A& which can be bound to a B object."_ So `A a{ B() }` is not aggregate initialization? – mada Aug 25 '22 at 18:24
  • @H.K. It is list initialization. I can explain it in more detail with reference from the standard if you ask a new separate question. Also, for any follow up question that was not originally asked in your this current question, feel free to ask a new question. See [What is the the best way to ask follow up questions?](https://meta.stackoverflow.com/questions/266767/what-is-the-the-best-way-to-ask-follow-up-questions). – Jason Aug 25 '22 at 18:30
  • I mean that `A a{ B() }` the object `a` is not an aggregate. Right? – mada Aug 25 '22 at 18:33
  • @H.K. Yes, `A` is an aggregate. For any follow up questions that were not originally asked in current question, kindly ask a separate question. Comment section is for discussing the currently asked question. I mean If you want to know the exact reason `A a{ B() }` works, then a new question should be created. – Jason Aug 25 '22 at 18:33
  • I will created a new one. – mada Aug 25 '22 at 18:38