diamond inheritance, constr. question

V

Victor Bazarov

Schüle Daniel said:
I encountered a strange behaviour in this code

#include <iostream>
#include <cstdlib>
#include <string>

using std::cout;
using std::endl;

#define virtual_Y
#define virtual_X

class A
{
public:
A(std::string s) { cout << (s+">A") << endl; } // <<<< 1
static std::string name;
};

std::string A::name = "<class A>";

class X : public virtual_X A
{
public:
X(std::string s) : A(s+">X") {}
};

class Y : public virtual_Y A
{
public:
Y(std::string s) : A(s+">Y") {}
};

class XX : public X
{
public:
XX(std::string s) : X(s+">XX") {}
};

class YY : public Y
{
public:
YY(std::string s) : Y(s+">YY") {}
};

class C : public XX, public YY
{
public:
C() : XX("C"), YY("C") {}
};

int main()
{
// are the same
cout << A::name << endl;
cout << C::XX::X::A::name << endl;
return EXIT_SUCCESS;
}

it compiles and runs fine without virtual inheritance
I do get the following hierarchy

A A
\ /
B C
\ /
D

Actually, I believe it's

A A
\ /
X Y
\ /
XX YY
\ /
C
what is strange, when I change

#define virtual_Y virtual
#define virtual_X virtual

and get this diamond hierarchy

A
/ \
B C
\ /
D

No, I believe you get

A
/ \
X Y
| |
XX YY
\ /
C
then my compiler (gcc-Version 3.3.1) tells me

mond:/pool/PROG/C++/C++/multiple # g++ -o main main.cpp -Wall -g
main.cpp: In constructor `XX::XX(std::basic_string<char,
std::char_traits<char>, std::allocator<char> >)':
main.cpp:35: error: no matching function for call to `A::A()'
main.cpp:12: error: candidates are: A::A(const A&)
main.cpp:14: error: A::A(std::basic_string<char,
std::char_traits<char>, std::allocator<char> >)
main.cpp: In constructor `YY::YY(std::basic_string<char,
std::char_traits<char>, std::allocator<char> >)':
main.cpp:41: error: no matching function for call to `A::A()'
main.cpp:12: error: candidates are: A::A(const A&)
main.cpp:14: error: A::A(std::basic_string<char,
std::char_traits<char>, std::allocator<char> >)
main.cpp: In constructor `C::C()':
main.cpp:47: error: no matching function for call to `A::A()'
main.cpp:12: error: candidates are: A::A(const A&)
main.cpp:14: error: A::A(std::basic_string<char,
std::char_traits<char>, std::allocator<char> >)

the bottom line is, it cannot find the approproite constructor
when I provide a default costructor then it compiles
A(std::string s="") { cout << (s+">A") << endl; } // <<<< 1

what is going on here?

Virtual base class subobjects are initialised by the most derived class'
constructor. If you do not _explicitly_ specify its initialisation in
the most derived class' c-tor's initialiser list, the default c-tor of
the virtual base class is used. If there is no default c-tor, the program
is ill-formed.

You've encountered this specific rule. You either need to provide the
default c-tor in 'A' when 'A' is virtually inherited, or you need to add
the initialiser for it in 'C':

class C : public XX, public YY
{
public:
C() : A("C"), XX("C"), YY("C") {}
};

V
 
?

=?iso-8859-1?q?Stephan_Br=F6nnimann?=

Schüle Daniel said:
Hello C++ NG, [snip]

A
/ \
B C
\ /
D

then my compiler (gcc-Version 3.3.1) tells me

mond:/pool/PROG/C++/C++/multiple # g++ -o main main.cpp -Wall -g
main.cpp: In constructor `XX::XX(std::basic_string<char,
std::char_traits<char>, std::allocator<char> >)':
main.cpp:35: error: no matching function for call to `A::A()'
main.cpp:12: error: candidates are: A::A(const A&)
main.cpp:14: error: A::A(std::basic_string<char,
std::char_traits<char>, std::allocator<char> >)
main.cpp: In constructor `YY::YY(std::basic_string<char,
std::char_traits<char>, std::allocator<char> >)':
main.cpp:41: error: no matching function for call to `A::A()'
main.cpp:12: error: candidates are: A::A(const A&)
main.cpp:14: error: A::A(std::basic_string<char,
std::char_traits<char>, std::allocator<char> >)
main.cpp: In constructor `C::C()':
main.cpp:47: error: no matching function for call to `A::A()'
main.cpp:12: error: candidates are: A::A(const A&)
main.cpp:14: error: A::A(std::basic_string<char,
std::char_traits<char>, std::allocator<char> >)

the bottom line is, it cannot find the approproite constructor
when I provide a default costructor then it compiles
A(std::string s="") { cout << (s+">A") << endl; } // <<<< 1

what is going on here?

Regards, Daniel

Virtual base must be initialized separetly in all derived classes,
this is the price one must pay for it.
Consider the diamond above and what you have implemented yourself:

B::B(std::string s) : A(s+">B") {}
C::C(std::string s) : A(s+">C") {}

In the constructor of D how should A be initialized, via B or via C?
Remember: A can be constructed only once
and the compiler can't make the choice, thus you'll need to tell him.
D::D(std::string s): A("D"), B("D"), C("D") {}

Regards, Stephan
(e-mail address removed)
Open source rating and billing engine for communication networks.
 
?

=?ISO-8859-1?Q?Sch=FCle_Daniel?=

Hello C++ NG,

I encountered a strange behaviour in this code

#include <iostream>
#include <cstdlib>
#include <string>

using std::cout;
using std::endl;

#define virtual_Y
#define virtual_X

class A
{
public:
A(std::string s) { cout << (s+">A") << endl; } // <<<< 1
static std::string name;
};

std::string A::name = "<class A>";

class X : public virtual_X A
{
public:
X(std::string s) : A(s+">X") {}
};

class Y : public virtual_Y A
{
public:
Y(std::string s) : A(s+">Y") {}
};

class XX : public X
{
public:
XX(std::string s) : X(s+">XX") {}
};

class YY : public Y
{
public:
YY(std::string s) : Y(s+">YY") {}
};

class C : public XX, public YY
{
public:
C() : XX("C"), YY("C") {}
};

int main()
{
// are the same
cout << A::name << endl;
cout << C::XX::X::A::name << endl;
return EXIT_SUCCESS;
}

it compiles and runs fine without virtual inheritance
I do get the following hierarchy

A A
\ /
B C
\ /
D


what is strange, when I change

#define virtual_Y virtual
#define virtual_X virtual

and get this diamond hierarchy

A
/ \
B C
\ /
D

then my compiler (gcc-Version 3.3.1) tells me

mond:/pool/PROG/C++/C++/multiple # g++ -o main main.cpp -Wall -g
main.cpp: In constructor `XX::XX(std::basic_string<char,
std::char_traits<char>, std::allocator<char> >)':
main.cpp:35: error: no matching function for call to `A::A()'
main.cpp:12: error: candidates are: A::A(const A&)
main.cpp:14: error: A::A(std::basic_string<char,
std::char_traits<char>, std::allocator<char> >)
main.cpp: In constructor `YY::YY(std::basic_string<char,
std::char_traits<char>, std::allocator<char> >)':
main.cpp:41: error: no matching function for call to `A::A()'
main.cpp:12: error: candidates are: A::A(const A&)
main.cpp:14: error: A::A(std::basic_string<char,
std::char_traits<char>, std::allocator<char> >)
main.cpp: In constructor `C::C()':
main.cpp:47: error: no matching function for call to `A::A()'
main.cpp:12: error: candidates are: A::A(const A&)
main.cpp:14: error: A::A(std::basic_string<char,
std::char_traits<char>, std::allocator<char> >)

the bottom line is, it cannot find the approproite constructor
when I provide a default costructor then it compiles
A(std::string s="") { cout << (s+">A") << endl; } // <<<< 1

what is going on here?

Regards, Daniel
 
?

=?ISO-8859-1?Q?Sch=FCle_Daniel?=

[..]
Virtual base must be initialized separetly in all derived classes,
this is the price one must pay for it.
Consider the diamond above and what you have implemented yourself:

B::B(std::string s) : A(s+">B") {}
C::C(std::string s) : A(s+">C") {}

In the constructor of D how should A be initialized, via B or via C?
Remember: A can be constructed only once
and the compiler can't make the choice, thus you'll need to tell him.
D::D(std::string s): A("D"), B("D"), C("D") {}

yes, this makes sense

so the rule would be ..
either the class which merges two branches of diamond together
should call the virtual base constructor explicitly or virtual
base class should have default constructor

Regards, Daniel
 
?

=?iso-8859-1?q?Stephan_Br=F6nnimann?=

Schüle Daniel said:
yes, this makes sense

so the rule would be ..
either the class which merges two branches of diamond together
should call the virtual base constructor explicitly or virtual
base class should have default constructor

No, virtual base classes must be initialized in ALL derived classes.
Somebody might provide a rationale for this rule. With
A
/ \
B C
\ / \
D CC
|
E

E must initialize A in its constructors:
class E : public class D {
public:
// E() {}
// ^ errror: no default constructor for A
E() : A("E") {}
};
and similar for class CC.

Regards, Stephan
(e-mail address removed)
Open source rating and billing engine for communication networks.
 

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

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,151
Latest member
JaclynMarl
Top