473,386 Members | 1,973 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,386 software developers and data experts.

Virtual inheritance and constructors in base classes with const membervariables

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
Jul 23 '05 #1
4 4586
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
Jul 23 '05 #2
Thomas Maier-Komor wrote:
Consider reading ISO/IEC 14882 chapter 10.


and even more appropriate sections 12.6 and 12.7
Jul 23 '05 #3
On Fri, 29 Apr 2005 11:54:20 +0200, Xavier <dont@like_sp.am> wrote:
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
Jul 23 '05 #4
On Fri, 29 Apr 2005 14:21:58 +0200, Thomas Maier-Komor
<ma******@lpr.no-spam.e-technik.tu-muenchen.de> wrote:

[...]
your problem is not at all related to templates. Consider the following
code:
As Xavier said:
The templates are not necessary to reproduce the error, but they help
tracing what's going on:


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
Jul 23 '05 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

0
by: Alexander Stippler | last post by:
I've got an inheritance structure with two coupled "dreaded diamonds" like shown below: A / \ / \ B C \ / \ \ / \ D E \ /
45
by: Ben Blank | last post by:
I'm writing a family of classes which all inherit most of their methods and code (including constructors) from a single base class. When attempting to instance one of the derived classes using...
3
by: Marcin Kalicinski | last post by:
Hi, Having the following program: #include <iostream> struct A { A() { std::cout << "A::A()\n"; } A(const A &) { std::cout << "A::A(const A &)\n"; }
12
by: mijobee | last post by:
I'm very new to c++ and just writing some code to learn. I've run into a problem, with a javaish design, and want to know if there is any possible solution without modifying the design. I've read...
5
by: toton | last post by:
Hi, I want a few of my class to overload from a base class, where the base class contains common functionality. This is to avoid repetition of code, and may be reducing amount of code in binary,...
7
by: Markus Svilans | last post by:
Hello, My question involves virtual functions and inheritance. Suppose we have a class structure, that consists of "data" classes, and "processor" classes. The data classes are derived from...
7
by: BeautifulMind | last post by:
In case of inheritence the order of execution of constructors is in the order of derivation and order of destructor execution is in reverse order of derivation. Is this case also true in case...
17
by: Jess | last post by:
Hello, If I have a class that has virtual but non-pure declarations, like class A{ virtual void f(); }; Then is A still an abstract class? Do I have to have "virtual void f() = 0;"...
0
by: =?Utf-8?B?Zmplcm9uaW1v?= | last post by:
Hi all, As I mentioned in a previous thread (see 'Dbghelp, symbols and templates' in microsoft.public.windbg), we created a powerful symbol engine using dbghelp to dump the contents of the stack...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.