I end up with a multiple inheritance diamond problem with the situation that there is no default constructor in the base class.
struct A {
A(int x) {}
};
struct B : virtual public A {
using A::A;
};
struct C : virtual public A {
using A::A;
};
struct D : virtual public B, public C {
D(int x) : B(x), C(x) {}
};
int main() {
D d(1);
}
The compiler is complaining that:
error: constructor for 'D' must explicitly initialize the base
class 'A' which does not have a default constructor
D(int x) : B(x), C(x) {}
But, I don't really have access to A from D... How can I fix that? Thanks
You do have an access to A since you're inheriting from it.
You would have to call the constructor of A in order to construct the object D.
Your D constructor should look something like that :
D(int x) : A(x), B(x), C(x) {}
Because of the multiple inheritance, constructors of B and C would ignore the A(x) part and you will have only one object which is D.
Related
Why can't I do this?
class A
{
public:
int a, b;
};
class B : public A
{
B() : A(), a(0), b(0)
{
}
};
You can't initialize a and b in B because they are not members of B. They are members of A, therefore only A can initialize them. You can make them public, then do assignment in B, but that is not a recommended option since it would destroy encapsulation. Instead, create a constructor in A to allow B (or any subclass of A) to initialize them:
class A
{
protected:
A(int a, int b) : a(a), b(b) {} // Accessible to derived classes
// Change "protected" to "public" to allow others to instantiate A.
private:
int a, b; // Keep these variables private in A
};
class B : public A
{
public:
B() : A(0, 0) // Calls A's constructor, initializing a and b in A to 0.
{
}
};
Leaving aside the fact that they are private, since a and b are members of A, they are meant to be initialized by A's constructors, not by some other class's constructors (derived or not).
Try:
class A
{
int a, b;
protected: // or public:
A(int a, int b): a(a), b(b) {}
};
class B : public A
{
B() : A(0, 0) {}
};
Somehow, no one listed the simplest way:
class A
{
public:
int a, b;
};
class B : public A
{
B()
{
a = 0;
b = 0;
}
};
You can't access base members in the initializer list, but the constructor itself, just as any other member method, may access public and protected members of the base class.
# include<stdio.h>
# include<iostream>
# include<conio.h>
using namespace std;
class Base{
public:
Base(int i, float f, double d): i(i), f(f), d(d)
{
}
virtual void Show()=0;
protected:
int i;
float f;
double d;
};
class Derived: public Base{
public:
Derived(int i, float f, double d): Base( i, f, d)
{
}
void Show()
{
cout<< "int i = "<<i<<endl<<"float f = "<<f<<endl <<"double d = "<<d<<endl;
}
};
int main(){
Base * b = new Derived(10, 1.2, 3.89);
b->Show();
return 0;
}
It's a working example in case you want to initialize the Base class data members present in the Derived class object, whereas you want to push these values interfacing via Derived class constructor call.
Why can't you do it? Because the language doesn't allow you to initializa a base class' members in the derived class' initializer list.
How can you get this done? Like this:
class A
{
public:
A(int a, int b) : a_(a), b_(b) {};
int a_, b_;
};
class B : public A
{
public:
B() : A(0,0)
{
}
};
While this is usefull in rare cases (if that was not the case, the language would've allowed it directly), take a look at the Base from Member idiom. It's not a code free solution, you'd have to add an extra layer of inheritance, but it gets the job done. To avoid boilerplate code you could use boost's implementation
Aggregate classes, like A in your example(*), must have their members public, and have no user-defined constructors. They are intialized with initializer list, e.g. A a {0,0}; or in your case B() : A({0,0}){}. The members of base aggregate class cannot be individually initialized in the constructor of the derived class.
(*) To be precise, as it was correctly mentioned, original class A is not an aggregate due to private non-static members
With virtual inheritance it is the most derived object's obligation to initialize not only direct base clases, but all virtual ancestor classes as well.
As an example, consider the following class hierarchy (minding that structs use public inheritance by default):
struct B {
B() = delete;
B(int);
};
struct D1 : virtual B {
D1(int x) : B(x) {}
};
struct D2 : virtual B {
D2(int x) : B(x) {}
};
If I were to intoduce a class D12 like this...
struct D12 : D1, D2 {
D12(int x) : D1(x), D2(x) {}
};
Compilation would fail, as D12 has to initialize B and because it doesn't do that, the compiler assumes that the default constructor (which is deleted) is supposed to be invoked. The solution would be to manually invoke the B::B(int) constructor:
struct D12 : D1, D2 {
D12(int x) : B(x), D1(x), D2(x) {}
};
I see a problem in that solution. The problem is that the declaration of class D1 does not contain either B or virtual. To properly implement this class we must somehow know that the classes D1 and D2 inherit from a virtual base class B. One could say that it should be assumed that if we use multiple inheritance than we will have to initialize some virtual base classes. However the problem persists when only seemingly using regular inheritance:
struct D : D1 {
// D still has to initialize B
D(int x) : B(x), D1(x) {}
};
As a sort of workaround I thought that maybe I could make D also inherit from B (virtually) to at least express this hidden dependency in the declaration:
struct D : virtual B, D1 {
D(int x) : B(x), D1(x) {}
};
Is this guaranteed to be equivalent to the previous code? Is there a better solution to this problem?
The compiler is complaining the constructor of D is deleted because of ill forming why ?
#include<iostream>
using namespace std;
class A
{
int x;
public:
A(int i) { x = i; }
void print() { cout << x; }
};
class B: virtual public A
{
public:
B():A(10) { }
};
class C: virtual public A
{
public:
C():A(10) { }
};
class D: public B, public C {
};
int main()
{
D d;
d.print();
return 0;
}
Output
main.cpp:37:4: error: use of deleted function 'D::D()' D d;
^ main.cpp:32:7: note: 'D::D()' is implicitly deleted because the default definition would be ill-formed: class D: public B, public C {
^
Due to the rules for initialization of virtual base classes,
class D: public B, public C {
};
is equivalent to:
class D: public B, public C {
public:
D() : A(), B(), C() {}
};
That's why you cannot create in instance of D.
Solution 1
Change A so it has a default constructor.
class A
{
int x;
public:
A(int i = 0) { x = i; }
void print() { cout << x; }
};
Solution 2
Change D to:
class D: public B, public C {
public:
D() : A(0), B(), C() {}
};
or a simpler version,
class D: public B, public C {
public:
D() : A(0) {}
};
That's because D inherits from A indirectly using virtual. A doesn't have a parameterless constructor so a compiler-generated constructor for D can't be made.
Note: this is mostly just adding a reference to the standard, in case anybody might care (but as usual for him, #R. Sahu's answer is quite accurate).
The standard specifies ([class.base.init]/13) that:
In a non-delegating constructor, initialization proceeds in the
following order:(13.1) — First, and only for the constructor of the
most derived class (6.6.2), virtual base classes are 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 classes in the derived class
base-specifier-list.(13.2) — Then, direct base classes are
initialized in declaration order as they appear in the
base-specifier-list (regardless of the order of the mem-initializers).
So, since A is a virtual base class, it's initialized directly by the most derived class (D). Only afterward, the direct base classes are initialized--but for anything to compile, the most derived class must be able to initialize the virtual base class(es).
There is one point some might find interesting in a case like this. Let's modify your class structure just a tiny bit, so we to the necessary initialization, and (importantly) initialize with a unique value in each constructor:
#include <iostream>
class A {
int i;
public:
A(int i) : i(i) {}
void show() { std::cout << "value: " << i << "\n"; }
};
class B : virtual public A{
public:
B() : A(10) {}
};
class C : virtual public A {
public:
C() : A(20) {}
};
class D : public B, public C {
public:
D() : A(0) {}
};
int main() {
D d;
d.show();
}
In this case, what exactly happens? We have three different constructors each "thinking" it's going to initialize the A object with a different value? Which one "wins"?
The answer is that the one in the most-derived constructor (D::D) is the one that' used to initialize the virtual base class object, so that's the one that "wins". When we run the code above, it should print 0.
This does not seem to work in C++11:
class B : public A
{
public:
B(const A& a)
: A(a) // parent constructor for passing the parameter
, B() // delegating constructor for init of other members
{};
// ...
};
gcc tells me that an initializer for a delegating constructor must appear alone.
How do I both call the constructor of the parent class with the parameter, and call the basic constructor of the B class? (I have a bunch of other constructors in B that need the same behavior).
Right now I am considering writing a private B::init() function and use it in all constructor bodies, but that tastes a bit much of C++03.
What is the preferred solution?
I believe the preferred way of delegation is the other way around, it's not meant to be used to refactor common parts of constructors, but rather to define the simpler one as a special case of a the more complex case.
So you should start with B(const A& a) and use this as delegation target.
class B : public A
{
public:
B() : B(A());
B(const A& a) : A(a) // parent constructor for passing the parameter
{};
};
You are calling A() anyways when creating B.
The rationale behind it is when you have two "partially specialized" c'tors, you wouldn't be able to use them to initialize the complex one. E.g:
class B : public A
{
public:
B() {};
B(int) : B() {};
B(double) : B() {};
B(double,int) : B(int), B(double) {}; // can't do it.
};
I believe the technical reason is explained in Bathsheba's answer. Look what would happen if you had a common part in B():
class B : public A
{
public:
B() {};
B(int) : B() {};
B(double) : B() {};
B(double,int) : B(int), B(double) {}; //ooops would get B() called twice!
};
It's the diamond problem known from inheritance. The solution is to reverse the logic.
class B : public A
{
public:
B() : B(0,0) {};
B(int a) : B(a,0) {};
B(double d) : B(0,d) {};
B(double a, int d) {/*full implementation*/};
};
B(const A& a) : A(a), B() does not make sense since B() will also initialise the base class A. That would essentially be a duplicate initialisation, which is an inherent contradiction.
The only real option for the language is to disallow anything else if you use a delegated constructor. That's what your compiler is telling you.
Have a problem with multiple inheritance. I have solved diamond problem:
class A
{
int m;
int n;
public:
A(int x, int y)
{
m = x; n = y
}
fA() {}
};
class B : virtual public A // B has fA(),fB()
{
public:
B(int k) : A(1, k) {}
fB() {}
};
class C : virtual public A // C has fA(),fC()
{
public:
C(int k) : C(2, k) {}
fC() {}
};
class D : public B, public C // D has fA(),fB(),fC()
{
public:
D(int k) : B(k),C(k),A(3,k)
};
This is working well. Problem with this:
class S : public B // S has fA(),fB()
{
public:
S() : B(6) {}
};
Compilator shows me: "error: no matching function for call to `A::A()'"
This code is working, but it doesn't satisfied me:
class S : public B // S has fA(),fB()
{
public:
S() : B(6),A(1,6) {}
};
In virtual inheritance, the constructor of virtual base is called from the constructor of most derived class:
class S : public B // S has fA(),fB()
{
public:
S() : B(6) {}
}; // ^ A base class is initialized at this point, before B
This also means that other explicit calls to A's constructor in initialization-lists further down the inheritance chain are ignored:
class B : virtual public A // B has fA(),fB()
{
public:
B(int k) : A(1, k) {}
fB() {} // ^^^^^^^ this call is ignored when B is a part of S object
};
If there's no explicit call to virtual base's constructor in initialization list of most derived class, the compiler will (of course) try to call the default constructor. But A doesn't have one and that's your problem.
One solution you already discovered yourself. The other is to write default constructor for A.