I have this example:
#include <iostream>
#define print(X) std::cout << X << std::endl
struct B1 {
B1(int _i = 5): i(_i) { print("B1 constructor"); };
int i;
};
struct B2 {
B2(int _j = 7): j(_j) { print("B2 constructor"); }
int j;
};
struct D : B2, B1 {
using B1::B1;
};
int main(void)
{
D d = D{10};
print("B1::i = " << d.i);
print("B2::j = " << d.j);
}
The output of this program is:
B2 constructor
B1 constructor
B1::i = 10
B2::j = 7
Per §11.9.4[class.inhctor.init]/1:
When a constructor for type B is invoked to initialize an object of a different type D (that is, when the constructor was inherited ([namespace.udecl])), initialization proceeds as if a defaulted default constructor were used to initialize the D object and each base class subobject from which the constructor was inherited, except that the B subobject is initialized by the inherited constructor if the base class subobject were to be initialized as part of the D object ([class.base.init]). The invocation of the inherited constructor, including the evaluation of any arguments, is omitted if the B subobject is not to be initialized as part of the D object. The complete initialization is considered to be a single function call; in particular, unless omitted, the initialization of the inherited constructor's parameters is sequenced before the initialization of any part of the D object.
Firstly, per §11.9.4/1
, since D
inherits constructor B1(int)
from base B1
, that inherited constructor can initialize subobject B1
; further, the parameter _i
is fully initialized before initializing any part of D
, hence the inherited constructor D::B1(int)
is selected by overload resolution which initializes B1::i
members by mem-initializer-list.
Since B2 constructor
is printed first, this means that B2
constructor is called before B1
. So how member B2::j
is initialized with default argument 7
not value 10
that's passed in the call D{10}
?
I'm not sure whether that happened, but I can't understand the sequence of execution in this case. and where this behavior is necessary in the standard.
B2
subobject is (default-)initialized, then theB1
constructor is passed 10. TheB2
is initialized first because it is mentioned first in the list of base classes.D
had the constructorD(int x): B2(), B1(x) {}
.B2::B2
is called first, as it should.i
is initialized first? There is nothing in the output to suggest that it is.B2
constructor. Perhaps removing the argument, and turn it intoB2(): j(7) { print("B2 constructor"); }
will help to clarify your misunderstanding. That is effectively what is called