JenyaKh JenyaKh - 2 months ago 24
C++ Question

c++11 value-initialization prior to aggregate-initialization

I try to understand the first accepted answer of @bolov to the question Deleted default constructor. Objects can still be created... sometimes [1]

It seems like I found a error there and so it messes up the whole explanation.

@bolov explains why this code SUCCEED to be compiled in c++11:

Scenario A

struct foo {
foo() = delete;
};

// All bellow OK (no errors, no warnings)
foo f = foo{};
foo f = {};
foo f{}; // will use only this from now on.


And why this code FAILS to be compiled in c++11:

Scenario C

struct foo {
foo() = delete;
foo(int) {};
};

foo f{}; // error call to deleted constructor


He says that the point is that the first one foo is an aggregate and the second one foo is not an aggregate.

Then he gives the excerpt from cppreference:


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


  • If T is an aggregate type, aggregate initialization is performed. This takes care of scenarios A B D E (and F in C++14)

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


    • All constructors that take std::initializer_list ...

    • otherwise [...] all constructors of T participate in overload resolution [...] This takes care of C (and F in C++11)
      ...





According to the excerpt when you write foo f { }; in scenario A you get aggregate-initialization. An it would be great. But in reality in c++11 (#3337 draft, closest to standard) you have different initialization order:


List-initialization of an object or reference of type T is defined as follows:


  • If the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.

  • Otherwise, if T is an aggregate, aggregate initialization is performed (8.5.1)




So foo f { }; in scenario A should result in value-initialization, that is, the DELETED default constructor will be called and the code should fail to be compiled.

Answer

As a result of Core Issue 1301, which was a defect against C++11, the precedence for list-initialization changed from:

List-initialization of an object or reference of type T is defined as follows:

  • If the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
  • Otherwise, if T is an aggregate, aggregate initialization is performed (8.5.1)

to:

List-initialization of an object or reference of type T is defined as follows:

  • If T is an aggregate, aggregate initialization is performed (8.5.1)
  • Otherwise, if the initialize list has no elements and T is a class type with a default constructor, the object is value-initialized.

So foo{} in Scenario A is still aggregate-initialization.