8

What I am looking to do is develop two different base classes which should not be inherited together in the one derived class. Is there any way I can enforce this at compile time?

class Base1 {};
class Base2 {};
class Derived1 : public Base1 {} // OK!
class Derived2 : public Base2, public Other {} // OK!
class Derived3 : public Base1, Base2 {} // Can I force the compiler to complain?

Derived1 d1; // OK!
Derived2 d2; // OK!
Derived3 d3; // Or can I force the compiler to complain here?

I'm aware that documentation is a good idea, just wondering if it is possible.

Community
  • 1
  • 1
Bingo
  • 3,785
  • 6
  • 23
  • 27

3 Answers3

8

You are setting up some kind of coupling between Base1 and Base2 in that they cannot both be derived from.

You could make them both derive from Base0 in which case if you derive from Base1 and Base2 you would get the multiple inheritance diamond so you would get a compiler error assuming you do not use virtual inheritance and you do not resolve the duplication.

That might solve your problem but I question why you are trying to do this.

(Base0 should not be a totally empty class as there has to be something there ambiguous to cause the compiler to complain. And of course you could resolve it so it won't totally prevent you deriving from both, just that it will generate the required compiler "error" if you do it by mistake).

An example might be:

class Base0 
{ 
  protected: 
    virtual ~Base0(){};
    virtual void abstractMethod() const = 0;
};

class Base1 : public Base0
{ 
   protected:
     virtual void abstractMethod() const;

   // rest of Base1
};

class Base2 : public Base0
{ 
   protected:
     virtual void abstractMethod() const;

   // rest of Base1
};

class Derived : public Base1, public Base2
{  
  // if I don't resolve abstractMethod it is ambiguous and the compiler will let me know
};
CashCow
  • 30,981
  • 5
  • 61
  • 92
  • +1 for " I question why you are trying to do this": any programmer feeling the need to go the multiple inheritance way should know the consequences – stijn Jan 06 '12 at 08:24
  • 1
    please give an example how you would make a compiler error show up with a common Base0 base class? doesn't seem obvious. just letting both Base1 and Base2 inherit from Base0 isn't giving any errors at all. – codeling Dec 19 '13 at 08:35
  • @nyalathotep That is no reason to randomly downvote an answer from a question that is 2 years old that the user has accepted. Just having an empty Base0 there will not cause an issue, there needs to be something there that is multiply inherited that causes the conflict. – CashCow Dec 19 '13 at 10:52
  • An answer being accepted doesn't necessarily mean it's correct. And it is a reason to downvote if the answer states something which is clearly wrong. Quoting "You could make them both derive from Base0 in which case if you derive from Base1 and Base2 you would get the multiple inheritance diamond so you would get a compiler error" - you would **not** get a compiler error in that case (and never did, not even 2 years ago). Clarify that, and I will retract my downvote. – codeling Dec 20 '13 at 12:55
  • An answer being accepted might mean the user tried it and it worked for them. They probably understood that my answer had its limitations, and the purpose was to prevent someone deriving from both accidentally and not to totally block workarounds. – CashCow Dec 30 '13 at 09:53
0

An interesting problem. I found a solution that works for Microsoft (R) C/C++ Optimizing Compiler Version 18.00.31101 for x64:

#include <iostream>
#include <assert.h>
using namespace std;

class SuperBase {
public:
SuperBase():count(0) {
    cout << "SuperBase constructor..." << endl;
}
~SuperBase() {}
protected:
int count;
};
class Base1:virtual SuperBase
{
public:
Base1() 
{
    SuperBase::count++;
    assert(SuperBase::count==1);
    cout << "Base1 constructor..." << endl;
}

~Base1() 
{
    cout << "Base1 Destructor..." << endl;
}
};  

class Base2:virtual SuperBase
{
public:
Base2() 
{
    SuperBase::count++;
    assert(SuperBase::count==1);
    cout << "Base2 constructor..." << endl;
}

~Base2() 
{
  cout << "Base2 Destructor..." << endl;  
}
};

class Derived : public Base1, public Base2 
{
public:
  Derived()
  {
  cout << "Derived constructor...."  << endl;
  }
  ~Derived()
  {
  cout << "Derived Destructor..." << endl;
   }
};

class Derived1 : public Base1
{
public:
  Derived1()
  {
  cout << "Derived1 constructor...."  << endl;
  }
  ~Derived1()
  {
  cout << "Derived1 Destructor..." << endl;
   }
};
class Derived2 : public Base2
{
public:
  Derived2()
  {
  cout << "Derived2 constructor...."  << endl;
  }
  ~Derived2()
  {
  cout << "Derived2 Destructor..." << endl;
   }
};



int main()
{
   cout << "Hello World" << endl; 
   Base1 b1; Base2 b2;
   Derived1 d1; 
   Derived2 d2;
   // Derived d; // - uncomment this line to get run-time error.

   return 0;
}
avb1003
  • 251
  • 2
  • 7
0

If you're now using some newer variant of C++ (not sure what you need for std::is_base_of), this might help you:

I faced a similar problem but had the luxury of constructing all possible instances of possible Derived classes through a common factory that hands out pointers to some other base class of Derived, which all possible derived classes share.

so I have this type of factory function templated on the actual derived type.

template <typename Derived>
    std::unique_ptr<Primordial_base>
    make_instance(nlohmann::json &data) {
      static_assert(
          not(std::is_base_of_v<Base1,Derived> and
std::is_base_of_v<Base2, Derived>),
         "Cant inherit from both Base1 and Base2.");
   
return std::make_unique<Derived>(data);
}

This is facilitated by the fact that all my classes have the same contructor arguments, namely a json type, I use N. Lohmann's library for that, but there are others.

This is not a method to strictly forbid ever deriving both Base1 and Base2 anywhere but maybe forbidding it only through this factory function is good enough for most use cases.

Eike
  • 359
  • 3
  • 8