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.
but I can't understand the sequence of execution
The important thing here is that the "only" the inherited ctor(B1::B1()
here) will use the D
's ctor's parameter to initialize its data member.
This here means that since you've inherited B1
's ctor, the parameter _i
in the ctor D::D(int _i)
will be passed as an argument to initialize i
of the B1
subobject.
Baiscally, your code class' D
is equivalent to writing:
struct D : public B2, public B1
{
inline D(int _i)
: B2(7)
//--vvvvvv------------->this is because you inherited B1's ctor
, B1(_i)
{
}
};
This is given in your quoted class.inhctor.init only which has been highlighted:
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.
(emphasis mine)
You can additionally verify this by inheriting B2
's ctor by doing using B2::B2
. Then your class D
will be equivalent to:
struct D : public B2, public B1
{
inline D(int _j)
//--vvvvvv------------>this happens when you inherit B2's ctor
: B2(_j)
, B1(5)
{
}
};