Order of initialization with mixed inline and constructor initializations

Viewed 171

[This question arised from this question]

Consider the following program:

#include <iostream>

struct Foo
{
    const size_t size;
    int* arr{ create_arr(size) };

    Foo(size_t s)
        : size{ set_size(s) }
    {
    }

    static int* create_arr(size_t s)
    {
        std::cout << "Creating array with size " << s << '\n';
        return new int[s];
    }

    static size_t set_size(size_t s)
    {
        std::cout << "Setting size to " << s << '\n';
        return s;
    }
};

int main()
{
    Foo f(10);
}

The inline initialization of arr depends on the value of size. But the value of size is only initialized in the constructor initializer list.

Both GCC and Clang handles it correctly, and will complain if the declaration order of size and arr is mirrored. I've also heard that MSVC will do the "right" thing (according to comments in the other question).

My question is: Is this well-defined?

I know that initialization is done in declaration order, but does it include inline initialization as well?

2 Answers

I know that initialization is done in declaration order, but does it include inline initialization as well?

Yes, it must, see class.base.init :

13 In a non-delegating constructor, initialization proceeds in the following order: (13.1) — First, and only for the constructor of the most derived class (4.5), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list. (13.2) — Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers). (13.3) — Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers). (13.4) — Finally, the compound-statement of the constructor body is executed. [ Note: The declaration order is mandated to ensure that base and member sub

Although the standard is not too specific here, that should be fully sufficient since default member initializers are initializers. The order in the class definition is the fundament here always. Having an exception here would be extremely contraditory to a lot of other parts of the standard in terms of initialization.

With an additional note also within this section, the standard emphasizes that via the focus on destruction:

[ Note: The declaration order is mandated to ensure that base and member subobjects are destroyed in the reverse order of initialization. — end note ]

So there's simply no further room for interpretations here.

Yes, it is well defined. I will use here the draft N4860 for C++20 as reference.

11.10.2 Initializing bases and members [class.base.init] §9 says (emphasize mine):

In a non-delegating constructor, if a given potentially constructed subobject is not designated by a meminitializer- id (including the case where there is no mem-initializer-list because the constructor has no ctorinitializer), then
(9.1) — if the entity is a non-static data member that has a default member initializer (11.4) and either
(9.1.1) — the constructor’s class is a union (11.5)...
(9.1.2) — the constructor’s class is not a union, and, if the entity is a member of an anonymous union... (9.2) — otherwise, if the entity is an anonymous union or a variant member (11.5.1), ...
(9.3) — otherwise, the entity is default-initialized (9.4).

This is consistent with 11.4 Class members [class.mem] §10

A brace-or-equal-initializer shall appear only in the declaration of a data member. (For static data members, see 11.4.8.2; for non-static data members, see 11.10.2 and 9.4.1). A brace-or-equal-initializer for a non-static data member specifies a default member initializer for the member...

Finaly, we find at 11.10.2 Initializing bases and members [class.base.init] §13 says (emphasize mine):

13 In a non-delegating constructor, initialization proceeds in the following order:
(13.1) — First, and only for the constructor of the most derived class (6.7.2), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.
(13.2) — Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).
(13.3) — Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
(13.4) — Finally, the compound-statement of the constructor body is executed.
[Note: The declaration order is mandated to ensure that base and member subobjects are destroyed in the reverse order of initialization. —end note]

My understanding is that the members of the class are initialized in declaration order whether they use a mem-initializer or not. If a member has no mem-initializer but has a brace-or-equal-initializer, it will be default initialized from that brace-or-equal-initializer after the members declared before it have been initialized.

So, if I have correctly read the 11.4 and 11.2 sub-clauses, the code in question has well defined behaviour.

Related