Reputation: 51
#include <iostream>
struct A
{
A(){std::cout<<"A()"<<std::endl;}
};
template<typename T>
struct B
{
A a;
T b;
B(){std::cout<<"B()"<<std::endl;}
};
int main()
{
B<B<B<int> > > Test;
return 0;
}
The odrer of calling constructors is
A()
A()
A()
B()
B()
B()
And i have no clue why is that. I thought it would be A B A B A B. Could you explain me why?
Upvotes: 5
Views: 92
Reputation: 10345
This is the expected behavior, because member initialization takes place before the body of the constructor. For realizing this, it is helpful to add the member initializers as well:
template<typename T>
struct B
{
A a;
T b;
B()
:
a(),
b()
{
std::cout<<"B()"<<std::endl;
}
};
To fully grasp the order of execution, let's add a dummy integer field. I also added a template to display the nesting. See http://ideone.com/KylQQb for a demo.
#include <cstdio>
struct A
{
A()
:
dummy(printf("Starting to construct A()\n"))
{
printf("Fully constructed A()\n");
}
int dummy;
};
template <typename T>
struct Nesting;
template <>
struct Nesting<int>
{
constexpr static int value = 0;
};
template <template <typename> class T, typename I>
struct Nesting<T<I>>
{
constexpr static int value = 1 + Nesting<I>::value;
};
template<typename T>
struct B
{
int dummy;
A a;
T b;
B()
:
dummy(printf("Starting to construct B() with nesting %d\n", Nesting<B<T>>::value)),
a(),
b()
{
printf("Fully constructed B() with nesting %d\n", Nesting<B<T>>::value);
}
};
int main()
{
B<B<B<int>>> Test;
return 0;
}
The output of this will be
Starting to construct B() with nesting 3
Starting to construct A()
Fully constructed A()
Starting to construct B() with nesting 2
Starting to construct A()
Fully constructed A()
Starting to construct B() with nesting 1
Starting to construct A()
Fully constructed A()
Fully constructed B() with nesting 1
Fully constructed B() with nesting 2
Fully constructed B() with nesting 3
Upvotes: 0
Reputation: 12635
First of all, let's analyze what you have here:
you have an object Test
of class B<B<B<int> > >
, which is:
class B<B<B<int> > > {
A a;
B<B<int> > b;
};
the second field of Test
, Test.b
is of class B<B<int> >
, which is:
class B<B<int> > {
A a;
B<int> b;
};
then you have the second field of Test.b
, Test.b.b
, which is of class B<int>
, which is:
class B<int> {
A a;
int b;
};
so the order of initialization is:
A()
for Test.a
.A()
for Test.b.a
.A()
for Test.b.b.a
.Test.b.b.b
is of type int
and has no constructor.B<int>()
for Test.b.b
.B<B<int> >()
for Test.b
.B<B<B<int> > >()
for Test
.Unfortunatelly, all the three constructors write on output the same thing: B()
, but they're different constructors for different classes.
Upvotes: 1
Reputation: 1736
This is because member variables must be initialized before the body of the constructor is executed. Consider the following example:
struct A {
int value;
// Here we explicitly initialize 'value' with 5
A() : value(5) { }
};
struct B {
A a;
B()
{
// This is perfectly valid and would print 5,
// because 'a' has already been implicitly initialized
// with its default constructor.
std::cout << a.value;
}
};
If this weren't the case, what value would you expect a
to have in B
's constructor? You'd run into all sorts of issues. Therefore, the default constructor of A
must be implicitly called before the body of B()
.
Essentially, to make it more explicit, this is what is happening:
// Initialize 'a' before body of constructor
B() : a()
{
std::cout << a.value;
}
Upvotes: 1
Reputation: 4715
This is actually straight forward, if it was like A B A B A B, then you would have trouble if you wanted to access b
from the constructor of B
, since the order you thought implies that first member a
gets instantiated, then ctor
runs, then b
gets initialized. In reality, every member is instantiated (constructed etc.) first, then, the constructors are called.
Upvotes: 2