breaking virtual inheritance avoiding explicit base ctor - c++

Following explanation and examples from here I have exemplary constructed the following inheritance model creating a diamond
class Base {
public:
int data_;
Base(int d) : data_(d) {}
Base() = deleted;
};
class A : public virtual Base {
public:
A() : Base(42) {};
};
class B : public virtual Base {
public:
B() : Base(24) {};
}
class AB : public A, public B {
public:
AB() : Base(-1) {};
}
so far so good; note, that AB() needs to call to the Base(int)-ctor now. This is kind of understandable, because having the alternative initializer branches of going through AB>>A>>Base or AB>>B>>Base would not result in a well defined behavior at that.
Lets branch out from class A into a side-branch before we even have closed the diamond:
class A_Child : public A {
public:
A_Child() : A() {}; // not permitted by compiler
}
This will not compile, as the compiler will explicitly ask for to specify the ctor Base(int) in the inializer list of A_Child.
I don't quite understand this behavior; as we are no longer virtually inheriting at this point and the path of initalization of A_Child>>A>>Base is not ambiguous.
It seems now for every furthermore derived class of A_Child I have to again specify the Base(int) initializer explicitly. This is kind of breaking the encapsulation as every code that derives from this class needs to know how the class at the very base acts and is implemented.
Is there any way to stop or to break the virtual inheritance once I branch into a side-line?

Related

Assert private inheritance in C++

I would like to assert that a base class is only inherited private.
class Base {
static_assert(...); //check if its derived privat
};
class PublicDerived : public Base {}; //this should fail
class PrivateDerived : private Base {}; //this should work
Is there a way to achieve this?
Base can also be a template class.
My problem was that I don't want that the destructor is called from this base class. For example from delete Base* or unique_ptr<Base>.
A protected destructor solves this issue.
Now the derived classes can use these functionalities the base class provides.
Public inheritation is still possible, but my use case is fullfilled.
class Base {
//...
protected:
constexpr Base() noexcept = default;
~Base() noexcept = default;
};

Initialize base class members from another base class

I tried splitting a data structure into several base classes to reuse the code and data members.
There i encountered the issue that it would greatly simplify my code if i could initialize the members of one base class with the members of another.
struct Base1
{
const int a;
Base1() : a(3) {}
};
struct Base2
{
const int b;
Base2() : b(a*2) {}
};
struct Derived :
Base1,
Base2
{
Derived() : Base1(), Base2() {}
};
Since the above is not possible, I'm searching for a way to achieve something similar, with changing the class Base1 and Base2 as little as possible.
How could I do that?
You'll need a clean interface. As Base2 is not related to Base1 in any way per your example, the dependency you describe shall be part of it's interface to the outward world. It is not an implementation detail.
As such the correct solution would be to provide an explicit (i don't mean the keyword) constructor:
Base2(int a) : b(a*2) {}
For Derived you then get:
Derived() : Base1(), Base2(a) {}
Notice that Base1 gets initialized before Base2 and as such a is already available.

Do I need to initialize base class in virtual inheritance classes' constructors for diamond inheritanceproblem?

I have the diamond problem resolved with virtual inheritance. The situation here is not a diamond but I want to understand the virtual inheritance:
class Base
{
public:
explicit Base(int arg)
: arg { arg }
{}
const int arg;
};
class Virtual : public virtual Base
{
public:
explicit Virtual(int _arg)
//: Base(_arg)
{}
};
class Target : public virtual Virtual
{
public:
explicit Target(int _arg)
: Base(_arg),
Virtual(_arg)
{}
};
So, the idea is that Virtual or Virtual2, inherited together by Target, will have single Base instance. Here I thought that calling Base constructor in Target constructor will create the instance needed and I could omit Base constructor in Virtual, but then I'm getting an error:
error: constructor for 'Virtual' must explicitly initialize the base class 'Base' which does not have a default constructor
So, is the constructor there really necessary if it's suppose not to be called?
In real situation I am making some changes to arguments with lambda within the constructors so I don't know if it needs to be repeated.
As you have written, both Virtual and Target needs
to provide a constructor for Base.
If you make Virtual abstract, then it does not need
to initialize Base any longer.
class Base
{
public:
explicit Base(int arg)
: arg(arg)
{}
private:
const int arg;
};
class Virtual : public virtual Base
{
public:
explicit Virtual(int arg)
{}
private:
virtual void foo() = 0;
};
class Target : public virtual Virtual
{
public:
explicit Target(int arg)
: Base(arg), Virtual(arg)
{}
private:
virtual void foo() override {}
};
It is necessary because otherwise the compiler doesn't know how to call the costructor of Virtual on its own, when the object is not a part of furher inheritance chain. You don't have to provide a constructor for Virtual, but when you do, you need to initialise Base as well. (If you don't provide a constructor for Virtual, you cannot make (sub)objects of this class, which makes it useless for most purposes).
If Virtual is an abstract class then its constructor can never be called on its own (only from further derived classes) and the compiler will let you skip initialisation of Base.

Why is super class empty constructor required but not called in a dreaded diamond situation?

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.

virtual inheritance and protected members access

I saw the example below in an SO question about preventing class inheritance, and I didn't understand why it should work : If I have a class "CTest" that inherits publicly CBase, the compilation fails because in constructor 'CTest::CTest()' 'CSealed::CSealed()' is protected. If I get rid off the virtual keyword, the compilation succeeds, why ?
class CSealed
{
protected:
CSealed()
{
}
};
class CBase : virtual CSealed
{
public:
CBase() {
}
};
class CTest : public CBase
{
public:
CTest() { std::cout << "TEST !!!!\n " << std::endl; }
};
The code you've posted,
class CSealed
{
protected:
CSealed()
{}
};
class CBase : virtual CSealed
{
public:
CBase()
{}
};
will not permit CBase to be derived from. That's because the virtual base class must be initialized by the (each) constructor of the most derived class. And since the virtual base is a private base class, it's not accessible to a class derived from CBase.
E.g., this class derivation,
class CTest
: public CBase
{};
should not compile it CTest is ever instantiated.
However, as a matter of fact, while it fails to compile with Visual C++ 2015, it does compile with MinGW g++ 6.3.0, so in practice it's not entirely reliable.
Instead of this old C++03 technique you can just use C++11 final:
class CBase final
{
public:
CBase()
{}
};