Virtual inheritance and constructors in base classes with const membervariables

X

Xavier

Hi,

I have a question, in a "dreaded diamond" situation, regarding the
following code:

---- Begin code

#include <iostream>

using namespace std;

template <int n>
class Animal
{
protected:
int extremities_;
const int flag_;
public:
Animal(const int &numext) :
extremities_(numext), flag_(1)
{cout << numext << " Extrms\n";}
Animal() {cout << "A NoArgs\n";} // Breaks things
// Animal() : flag_(0)
// {cout << "A NoArgs\n";} // Uncomment to fix code
};

template <int n>
class Bird : public virtual Animal<n>
{
public:
Bird() : Animal<n>(2) {cout << "Bird Args\n";}
};

template <int n>
class CrippledHorse : public virtual Animal<n>
{
public:
CrippledHorse(const int &numext) :
Animal<n>(numext) {cout << "CH Args\n";}
CrippledHorse() {cout << "CH NoArgs\n";}
};

template <int n>
class CrippledPegasus :
public virtual CrippledHorse<n>, public virtual Bird<n>
{
public:
CrippledPegasus(const int &numext) :
Animal<n>(numext) {cout << "CP Args\n";}
};

int main(int argc, char *argv[])
{
CrippledPegasus<0> foo(5);

return 0;
}

---- End code

When I try to compile it with g++ (GCC) 3.4.2 20041017 (Red Hat
3.4.2-6.fc3) I get the following error messages:

tst.cpp: In constructor `Animal<n>::Animal() [with int n = 0]':
tst.cpp:33: instantiated from `CrippledHorse<n>::CrippledHorse() [with
int n = 0]'
tst.cpp:42: instantiated from `CrippledPegasus<n>::CrippledPegasus(const
int&) [with int n = 0]'
tst.cpp:47: instantiated from here
tst.cpp:15: error: uninitialized member `Animal<0>::flag_' with `const'
type `const int'

The templates are not necessary to reproduce the error, but they help
tracing what's going on: after fiddling with it, I think the problem
comes because, for a reason unknown to me, CrippledHorse calls the
Animal constructor without any arguments, which doesn't initialize the
const member variable producing the compiler error.
If my understanding of virtual inheritance is correct, CrippledPegasus
is the responsible of calling the Animal constructor. Thus, neither
CrippledHorse nor Bird should call any of the Animal constructors. In
particular, CrippledHorse should have no reason to deal with Animal()
(the version with no arguments).
However, CrippledHorse does have something to do with Animal() , as
proven by the fact that initializing as in
Animal() : flag_(0)
{cout << "A NoArgs\n";} // Uncomment to fix code
fixes the problem.
My puzzlement is even higher when, after running the version of the
code that compiles, I see that the line "A NoArgs" never appears in the
output (as it should be since that constructor isn't supposed to be
called anyway).
So, my question is, is this the standard C++ behavior and my ideas are
wrong? Could this be a problem with the way the compiler implements
things? (sorry, I don't have access to a different compiler yet)
I would sleep much better at night if I didn't have to define Animal()
at all, since that hinders readability because it's never supposed to be
called...
Thanks,

Xavier
 
T

Thomas Maier-Komor

Xavier wrote:
[ code removed ]
The templates are not necessary to reproduce the error, but they help
tracing what's going on: after fiddling with it, I think the problem
comes because, for a reason unknown to me, CrippledHorse calls the
Animal constructor without any arguments, which doesn't initialize the
const member variable producing the compiler error.
If my understanding of virtual inheritance is correct, CrippledPegasus
is the responsible of calling the Animal constructor. Thus, neither
CrippledHorse nor Bird should call any of the Animal constructors. In
particular, CrippledHorse should have no reason to deal with Animal()
(the version with no arguments).
However, CrippledHorse does have something to do with Animal() , as
proven by the fact that initializing as in
Animal() : flag_(0)
{cout << "A NoArgs\n";} // Uncomment to fix code
fixes the problem.
My puzzlement is even higher when, after running the version of the
code that compiles, I see that the line "A NoArgs" never appears in the
output (as it should be since that constructor isn't supposed to be
called anyway).
So, my question is, is this the standard C++ behavior and my ideas are
wrong? Could this be a problem with the way the compiler implements
things? (sorry, I don't have access to a different compiler yet)
I would sleep much better at night if I didn't have to define Animal()
at all, since that hinders readability because it's never supposed to be
called...
Thanks,

Xavier

Hi Xavier,

your problem is not at all related to templates. Consider the following
code:
#include <iostream>

using namespace std;

struct ABC
{
ABC()
{
cout<<"ABC::ABC()" << endl;
}
};

struct A : virtual public ABC
{
A()
: ABC()
{
cout << "A::A()" << endl;
}
};

struct B : virtual public ABC
{
B()
: ABC()
{
cout << "B::B()" << endl;
}
};


struct C : public A, public B
{
C()
: A()
, B()
{
cout << "C::C()" << endl;
}
};

int main()
{
C c;
return 0;
}


It will print:
ABC::ABC()
A::A()
B::B()
C::C()


The compiler is responsible that the virtual base class gets constructed
exactly once. But this still means that every derived class that
has a virtual base class needs to call the base class' constructor.
Here A and B call ABC's constructor, but it only gets executed once.
C does not call ABC's constructor and it also must not call it, because
it is not derived directly from it.

Hope that helps.
Consider reading ISO/IEC 14882 chapter 10.


Cheers,

Tom
 
K

Kanenas

Hi,

I have a question, in a "dreaded diamond" situation, regarding the
following code: [...]

When I try to compile it with g++ (GCC) 3.4.2 20041017 (Red Hat
3.4.2-6.fc3) I get the following error messages:
[...]

Considering that g++ 3.3.2 on OpenBSD 3.6 compiles the example with no
errors and what Stroustrup has to say in section 15.2.4.1:
"For example, the language ensures that a constructor of a
virtual base is called exactly once. The constructor of a
virtual base is invoked (implicitly or explicitly) from the
constructor for the complete object (the constructor for the
most derived class)."
I'd say it's a compiler issue, not a language issue.
However, CrippledHorse does have something to do with Animal() , as
proven by the fact that initializing as in
Animal() : flag_(0)
{cout << "A NoArgs\n";} // Uncomment to fix code
fixes the problem.
My puzzlement is even higher when, after running the version of the
code that compiles, I see that the line "A NoArgs" never appears in the
output (as it should be since that constructor isn't supposed to be
called anyway).

Sounds like the bytecode g++ 3.4.2 emits is correct; the error is
probably just in the overload-resolution components of g++ (or the
components which check that const variables get initialized or
whatever spits out the error).
So, my question is, is this the standard C++ behavior and my ideas are
wrong? Could this be a problem with the way the compiler implements
things? (sorry, I don't have access to a different compiler yet)

You're right. The error from g++ 3.4.2 is non-standard behavior.
Fortunately, you found a workaround.

Kanenas
 
K

Kanenas

On Fri, 29 Apr 2005 14:21:58 +0200, Thomas Maier-Komor

[...]
your problem is not at all related to templates. Consider the following
code:

As Xavier said:
The compiler is responsible that the virtual base class gets constructed
exactly once. But this still means that every derived class that
has a virtual base class needs to call the base class' constructor.
Here A and B call ABC's constructor, but it only gets executed once.

Construction generally consists of allocating space and then invoking
a constructor, so the statement that a virtual base's constructor
needs to be called (or rather, invoked) more than once is a
contradiction. Considering that to invoke a constructor will execute
it, it's contradictory to say that ABC's constructor is called (or
rather, invoked) twice when constructing a C yet is executed exactly
once.
C does not call ABC's constructor and it also must not call it, because
it is not derived directly from it.
A class can (explicity or implicitly) invoke the constructor for a
virtual base class in an initializer list even if not a direct
descendent; indeed, it must.

As you later suggested, I looked at section 12.6 and found:
12.6.2.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."

This, too, implies the constructors for A and B will not invoke ABC's
constructor when constructing a C.

Kanenas
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top