Understanding virtual base classes and constructor calls
There is always just one constructor call, and always of the actual, concrete class that you instantiate. It is your responsibility to endow each derived class with a constructor which calls the base classes' constructors if and as necessary, as you did in B
's constructor.
Update: Sorry for missing your main point! Thanks to ildjarn.
However, your B
inherits virtually from A
. According to the standard (10.1.4 in the FIDS), "for each distinct baseclass that is specified virtual, the most derived object shall contain a single base class subobject of that type". In your case this means that when constructing the base, your class F
immediately calls A
's default constructor, not B
's.
(Why) Is virtual base class constructor call required in pure virtual derived class?
Design issues (which clearly exist here) aside, I fully agree with your reasoning that, because Left
is an abstract class, no Left
constructor will ever have to call any Base
constructor and thus it is odd that it is required.
In fact, I tested your code with several compiler versions and it does compile just fine with gcc 7.1 and onwards, with clang 3.4.1 and onwards, and with all available msvc versions.
So, my assumption is, that this was just a bug in earlier compiler versions. Can anyone confirm this?
Also note, if you change virtual void leftsMethod() = 0;
to virtual void leftsMethod() {}
, so that Left
is not abstract anymore, the error returns, even with the latest versions. And this makes perfectly sense, as now you could instantiate a Left
instance and thus it would be up to Left
's constructor to call one of Base
's constructors.
Possible workaround
If you cannot switch to a newer compiler and if you cannot alter the implementation of Base
, this may be a working solution for you:
Define a dummy instance of CustomType
somewhere. Then, you can provide the "required" default constructors like this:
class CustomType{};
class Base
{
public:
Base( CustomType& obj ) : refObj_( obj ) {}
private:
CustomType& refObj_;
};
static CustomType dummy;
class Left : public virtual Base
{
protected:
Left() : Base(dummy) {}; // note here
public:
virtual void leftsMethod() = 0;
};
class Right : public virtual Base
{
protected:
Right() : Base(dummy) {}; // and here
public:
virtual void rightsMethod() = 0;
};
class Bottom : public Left, public Right
{
public:
Bottom( CustomType& obj ) : Base( obj ), Left(), Right() {}
virtual void leftsMethod() override {}
virtual void rightsMethod() override {}
};
void test()
{
CustomType c;
Bottom b(c);
}
This is just to make the compiler happy, your argument that these constructors will never call Base(dummy)
still holds.
Note however, that Base
should really have a virtual destructor! I don't know if you just didn't include it here for brevity or if it really doesn't have one. If it doesn't, it's a very bad idea to build a class hierarchy on top of it.
Why do the constructor of the derived classes want to initialize the virtual base class in C++?
The constructor of virtual base is constructed. It is constructed conditionally. That is, the constructor of the most derived class calls the constructor of the virtual base. If - this is the condition - the derived class with virtual base is not the concrete class of the constructed object, then it will not construct the virtual base because it has already been constructed by the concrete class. But otherwise it will construct the virtual base.
So, you must correctly initialise the virtual base class in constructors of all derived classes. You simply must know that specific initialisation doesn't necessarily happen in case the concrete class is not the one which you are writing. The compiler doesn't and cannot know whether you will ever create direct instances of those intermediate classes, so it cannot simply ignore their broken constructors.
If you made those intermediate classes abstract, then the compiler would know that they are never the most concrete type and thus their constructor would not be required to initialise the virtual base.
Why must virtual base classes be constructed by the most derived class?
Because it avoids this:
class A {
public:
A(int) {}
};
class B0: virtual public A {
public:
B0(): A(0) {}
};
class B1: virtual public A {
public:
B1(): A(1) {}
};
class C: public B0, public B1 {
public:
C() {} // How is A constructed? A(0) from B0 or A(1) from B1?
};
wrong constructor called for virtual base class of virtual base class
The Answer is given in the comments by Sam Varshavchik.
The concept of passing the argument from each constructor to it's direct parent was the wrong way of handling the issue.
I was under the wrong impression, that I can only access the direct parents of each class. Sam Varshavchik's comment helped me to see the real problem and look for it in the right places. In one of the many classes, there was one non-virtual inheritance that made it impossible to access the constructor of the base class.
Forced to call the base constructor when using virtual inheritance although it will never be called?
In virtual inheritance, the most-derived class has to directly call all of its ancestor constructors. Since Base
doesn't have a default constructor, Middle1()
and Middle2()
can't compile if they can't pass an int&
to Base()
.
However, in the code you have shown, there is no reason for Base()
to take an int
by reference. Pass it by value instead, and then Middle1()
and Middle2()
can pass 0 to Base()
:
class Base
{
private:
int value;
protected:
Base(int value = 0) { this->value = value; }
};
class Middle1 : virtual public Base
{
protected:
Middle1() { }
};
class Middle2 : virtual public Base
{
protected:
Middle2() { }
};
class Foo : public Middle1, public Middle2
{
public:
Foo(int value) : Base(value) { }
};
Though, I would suggest passing a pointer (or a std::optional
) instead:
class Base
{
private:
int value;
protected:
Base(int* avalue = nullptr) { if (avalue) this->value = *avalue; }
};
class Middle1 : virtual public Base
{
protected:
Middle1() { }
};
class Middle2 : virtual public Base
{
protected:
Middle2() { }
};
class Foo : public Middle1, public Middle2
{
public:
Foo(int& value) : Base(&value) { }
};
Related Topics
Boost Libraries in Multithreading-Aware Mode
How to Write Video File in Opencv 2.4.3
How to Use the Ansi Escape Code for Outputting Colored Text on Console
How to Know Underlying Type of Class Enum
Is There an Non-Short Circuited Logical "And" in C++
Direct C Function Call Using Gcc's Inline Assembly
What Is the Size of Float and Double in C and C++
How to Make a C++ Console Program Exit
Why Are C++ Int and Long Types Both 4 Bytes
What Is the Practical Use of Pointers to Member Functions
How to Set Error_Code to Asio::Yield_Context
Duplicate Const Qualifier Allowed in C But Not in C++
Concurrent Writes in the Same Global Memory Location
Vector of Class Without Default Constructor
Check If a Type Is Passed in Variadic Template Parameter Pack