Reputation: 331
Let's say I have the following:
class A {
B member1;
C member2;
public:
A();
};
class B {
public:
C& ref_to_c;
B( C& ref_to_c );
};
class C {
...
};
B requires that a reference to C be provided on its constructor. If class A provides C, is it legal to specify A's initialiser list as the following...
A() : member1( B( member2 ) ) {}
That is to say, does member2 exist in the initialiser list phase, or is this undefined behaviour?
Upvotes: 10
Views: 416
Reputation: 20730
You are making up member1
that contains a reference to memebr2
.
That is not constructed yet, but the compiler already knows where it will be (so it can provide a reference).
It will work, but will be UB if you try -for example in B ctor- to access the ref_to_c
value in some kind of expression, since the reference is actually aliasing an uninitialized memory, that will be initialized during member2 construction, that would happen later.
The same problem will be in B destructor, where member2
will be destroyed before ref_to_c
.
It will be better if you swap member2 and member1 in A, so that you will initialize the reference with a constructed object, making every possible usage, defined.
Upvotes: 4
Reputation: 36483
Intialization is as follows:
5 Initialization shall proceed in the following order:
— First, and only for the constructor of the most derived class as described below, virtual base classes shall be 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 class names in the derived class base-specifier-list.
— Then, direct base classes shall be initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).
— Then, nonstatic data members shall be initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
— Finally, the body of the constructor is executed. [Note: the declaration order is mandated to ensure that base and member subobjects are destroyed in the reverse order of initialization. ]
Which basically means that member1
will always be initialized before member2
. So, B
's constructor will run first.
Even if you'd call them in reverse order explictly in A
's constructor:
A() : member2(foo), member1(bar) {}
it doesn't make a difference. Now, referencing an uninitialized object is not UB by itself but it can be depending on B
's constructor. You should switch the order of declaration:
C member2;
B member1;
Upvotes: 14