This question already has an answer here:
Why is Default constructor called in virtual inheritance?
(1 answer)
Closed 6 years ago.
I think an example would describe my problem the best.
struct Base {
Base() = delete;
Base(int x) : x(x) {}
int x;
};
struct Derived1 : virtual Base {
Derived1(int y) : Base(5), y(y) {}
int y;
};
struct Derived2 : virtual Base {
Derived2(int z) : Base(10), z(z) {}
int z;
};
struct Derived3: Derived1, Derived2 {
public:
Derived3(int y, int z) : Derived1(y), Derived2(z) {}
};
And an error I get:
In constructor ‘Derived3::Derived3(int, int)’:
error: use of deleted function ‘Base::Base()’
Derived3(int y, int z) : Derived1(y), Derived2(z) {}
I do not understand why this error occurs. In my opinion all base classes are actually get initialized in this example via their constructors (explicitly Derived1 and Derived2 and implicitly Base through Derived2 (I am not sure here, maybe through Derived1)).
Well, let's do what compiler tells me.
struct Derived3: Derived1, Derived2 {
public:
Derived3(int y, int z) : Base(-1), Derived1(y), Derived2(z) {}
};
It compiles and if I do now this:
Derived3 d = Derived3(7, 9);
std::cout << d.x << std::endl;
std::cout << d.y << std::endl;
std::cout << d.z << std::endl;
I get -1, 7, 9. And it is not what I wanted at all. An idea was to initialize base class with one of its derived classes and I was expecting the first number to be 5 or 10.
So, here is my question:
Why I am forced to explicitly call Base constructor when It is already done in one of the derived classes?
More specifically, as I have multiple inheritance and virtual inheritance, I believe that any instance of Derived3 has exactly one copy of instance of Base class. And I was expecting that this copy of Base is initialized in the most derived class of it (Derived1 or Derived2), but as I clearly can see it does not work in this way =( Where am I wrong?
When you use virtual inheritance, there is only 1 copy of Base. Who should initialize that copy, Derived 1 or Derived 2? There is no way to know. That is why you are forced to do it yourself in Derived 3, so there can be no ambiguity. That is also why you get the output you get instead of 5 or 10.
Without virtual inheritance both Derived 1 and Derived 2 would have their own copies of Base that they would be responsible for, therefore there is no ambiguity. When you force them to inherit from the same base Derived 3 has to take ownership of Base in order to resolve the ambiguities... virtual inheritance is weird at best.
Related
I am trying to achieve the following design, which is a dreaded diamond situation:
struct super_base
{
super_base(int a) { b = a; }
int b;
};
struct base : virtual super_base
{};
struct other_base : virtual super_base
{};
struct derived : base, other_base
{
derived(int a) : super_base{a} {}
};
int main() {}
Which doesn't work. The error for the above code using Clang is quite
good at explaining the mistake:
error: call to implicitly-deleted default constructor of 'base'
derived(int a) : super_base{a} {}
^
note: default constructor of 'base' is implicitly deleted because base
class 'super_base' has no default constructor
So I added an empty constructor for super_base, and it works:
#include<iostream>
#include<stdexcept>
struct super_base
{
super_base() { throw std::logic_error{"Constructor should not be called."}; };
super_base(int a) { b = a; }
int b;
};
struct base : virtual super_base
{};
struct other_base : virtual super_base
{};
struct derived : base, other_base
{
derived(int a) : super_base{a} {}
};
int main() { std::cout << derived(10).b << '\n'; }
But why does this not throw ?
P.S.: I purposefully used a dreaded diamond pattern to illustrate the use of virtual inheritance. The problem remains the same with single inheritance.
P.P.S.: The compiler used is Clang 3.9.1. The results are the same with
GCC 6.3.1.
struct base:super_base {}:
this tries to create some constructors. One of them it tries to create is base::base().
If super_base has no super_base::super_base(), this constructor is deleted.
If we have super_base::super_base()=default or {}, then base::base() by default exists, even if you don't =default it.
The same thing happens in other_base.
And your derived class tries to call the base object constructors, which are deleted, which gives you an error.
Now, why isn't it called? When you have a virtual base class, the constructor is only called once. Intermediate types which call the virtual base classes constructor have their calls ignored.
So we have derived() calling base(), base() calling super_base(), but that call is ignored because of virtual inheritance.
derived()'d call of super_base(int) is used instead.
Now, why are these the rules? Because C++ doesn't have the concept of "constructor that can only be called if you are a derived class of this class explicitly calling a specific base constructor". Virtual inheritance is not quite as full featured as you might like.
This question already has an answer here:
Why is Default constructor called in virtual inheritance?
(1 answer)
Closed 9 years ago.
C++11 standard provides a way to inherit constructors from the base class. My question is regarding earlier standards. Suppose I inherit constructors in the following way:
class Base {
public:
Base() {};
Base(int b) { a = ++b;}
virtual int foo() {return 0;}
int a;
};
class A : public virtual Base {
public:
A() {}
A(int b): Base(b) {}
int foo() {return a*a;}
};
class C : public A {
public:
C() {}
C(int b ): A(b) {}
int foo() { return (a*a + a);}
};
Note that I am having virtual inheritance of the Base class. Now when I try to initialize a pointer to an object of type C then instead of calling Base(b) constructor, the code ends up calling Base() constructor. Here is the main function that I used:
int main(){
C *c = new C(5);
std::cout << c->Base::a << std::endl;
}
The output value of "a" is 0. However, when I remove the virtual keyword while inheriting the Base class, then Base(b) constructor is called and the value of "a" is 6. Can someone help me in understanding what is going on? Why is it that with virtual inheritance default constructor is called?
Virtual base classes are initialised based on the member initialiser list of the constructor of the most derived class.
In your case, when you're creating an instance of C, its Base subobject will be initialised based on the member-initialiser list in C's constructor; and since Base is not listed there, it will be default-initialised.
If you were creating an instance of A, then indeed A's member-initialiser list would be used.
So to call the Base constructor which you want, you'd have to modify C's constructor like this:
C(int b ): A(b), Base(b) {}
This question already has answers here:
c++ virtual inheritance
(3 answers)
Closed 10 years ago.
The output of the program below is:
5
5
#include <iostream>
using namespace std;
struct A
{
public:
int myInt;
A(int n): myInt(n){}
A(): myInt(5) {}
};
class B : virtual public A
{
public:
B(int n):A(10) {}
B():A(10) {}
};
class C : virtual public A
{
public:
C(int n):A(3*n) {}
};
class D : public B, public C
{
public:
D(int n=90) : C(2*n), B(n) {}
};
class E : public D
{
public:
E(int n=20):D(n-1) {}
};
int main()
{
D d(100);
cout << d.myInt << endl;
E e;
cout << e.myInt << endl;
return 0;
}
Consider the object d. From what I understand the inheritance is constructed based on the order of the inheritance list (rather than the initialization list) so B class is constructed first with the param 100 which goes to class A with the parameter 10. So now A sets myInt to the value 10. The same goes for Class c and because myInt is virtual then it is set to the number 600. I never expected 5. why is this happening?
See article in parashift:
Because a virtual base class subobject occurs only once in an
instance, there are special rules to make sure the virtual base
class's constructor and destructor get called exactly once per
instance. The C++ rules say that virtual base classes are constructed
before all non-virtual base classes. The thing you as a programmer
need to know is this: constructors for virtual base classes anywhere
in your class's inheritance hierarchy are called by the "most derived"
class's constructor.
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
gcc c++ virtual inheritance problem
Hi all,
I'm wondering about how the compiler would handle different initialization values when using multiple inheritance from a virtual base class. Consider the notorious 'diamond of dread' inheritance scheme:
Base
/ \
/ \
D1 D2
\ /
\ /
Join
In order to avoid having two copies of Base in Join, I use virtual inheritance for D1 and D2 (see e.g. here). Now, lets say Base is not abstract, but has a member field, which is initialized in its constructor:
class Base {
public:
Base(int x_) {x = x_;};
virtual ~Base(){};
public:
int x;
};
class D1 : public virtual Base {
public:
D1() : Base(1) {};
virtual ~D1(){};
};
class D2 : public virtual Base {
public:
D2() : Base(2) {};
virtual ~D2(){};
};
class Join : public D1, public D2 {
public:
Join(){};
~Join(){};
};
int main()
{
Join j;
cout << j.x << endl;
return 0;
}
Will the output be 1, 2, or is it compiler-dependent?
It shoudn't compile. Virtual bases are initialized by the most derived class which is Join. Join doesn't explicitly initialize Base but Base has no accessible default constructor.
[It also won't compiler because definitions of classes need to be terminated with a ; but I've assumed that this is a typo. main should also return int and I've assumed that j.x is a typo for j->x.]
When you have virtual inheritance it is the final class that must initialise the virtual base class.
Therefore the constructor of Join must construct Base:
class Join : public D1, public D2
{
public:
Join() : Base(3){} // or whatever value
~Join(){}
};
It is the exception to the rule that classes only normally initialise their immediate base-classes.
(Aside from that main should return int and you would need to do j->x not j.x as j is a pointer, as well as the fact you must delete it as you called new)
Since the Base does not have an implicit constructor and both C1 and C2 are virtual bases, you would have to change it like this (and also add semicolons after the rest of the class declarations as pointed out by Charles Bailey)
class Join : public D1, public D2 {
public:
Join() : Base(3) {};
~Join(){};
};
int main()
{
Join j;
cout << j.x << endl;
}
Then it would print 3 to the standard output
class Base
{
public:
Base(){}
Base(int k):a(k)
{
}
int a;
};
class X:virtual public Base
{
public:
X():Base(10){}
int x;
};
class Y:virtual public Base
{
public:
Y():Base(10){}
int y;
};
class Z:public X,public Y
{
public:
Z():X(10){}
};
int main()
{
Z a;
cout << a.a;
return 1;
}
In the above case, for Z():X(10){} Base(int k):a(k) is not calling, but when i change to Z():Base(10){} the Base(int k):a(k) is called. Why ?
Thank you.
Because you used the virtual keyword - that's exactly what it does.
You have to explicitly initialize Base in the initializer list of Z in order to disambiguate between the initialization in X and the initalization in Y.
See this question. The gist is, that when using virtual inheritance you have to call the base class constructor explicitly.
The initializer list in the most derived constructor is used to initialize your base classes. Since class Z inherits from class X and Y which inherits from a common base class, the virtual keyword is used to create only a single subobject for the base class in order to disambiguate when accessing the data member a.