【问题标题】:Is it possible to prevent multiple inheritance of specific base classes at compile time?是否可以在编译时防止特定基类的多重继承?
【发布时间】:2012-02-04 00:02:40
【问题描述】:

我想做的是开发两个不同的基类,它们不应该在一个派生类中一起继承。有什么办法可以在编译时强制执行吗?

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?

我知道documentation 是个好主意,只是想知道它是否可能。

【问题讨论】:

  • 我认为拥有一个私有构造函数就可以了。但它也会禁用 Derived2 案例。
  • 写“不要这样做!”还不够吗?在文档中?
  • 开发者阅读文档?

标签: c++ multiple-inheritance


【解决方案1】:

您正在 Base1 和 Base2 之间建立某种耦合,因为它们不能同时派生。

你可以让它们都从 Base0 派生,在这种情况下,如果你从 Base1 和 Base2 派生,你会得到多重继承菱形,所以假设你不使用虚拟继承并且你不解决重复,你会得到一个编译器错误。

这可能会解决您的问题,但我质疑您为什么要这样做。

(Base0 不应该是一个完全空的类,因为必须有一些模棱两可的东西导致编译器抱怨。当然你可以解决它,所以它不会完全阻止你从两者派生,只是它会如果你做错了,会产生所需的编译器“错误”)。

一个例子可能是:

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
};

【讨论】:

  • +1 表示“我质疑你为什么要这样做”:任何觉得需要采用多重继承方式的程序员都应该知道后果
  • 请举一个例子,你将如何让一个通用的 Base0 基类显示编译器错误?似乎并不明显。只是让 Base1 和 Base2 都从 Base0 继承根本不会产生任何错误。
  • @nyalathotep 没有理由随机否决用户已接受的 2 年前问题的答案。只是有一个空的 Base0 不会导致问题,需要有一些被多重继承的东西导致冲突。
  • 被接受的答案并不一定意味着它是正确的。如果答案陈述了明显错误的内容,则有理由投反对票。引用“你可以让它们都从 Base0 派生,在这种情况下,如果你从 Base1 和 Base2 派生,你会得到多重继承菱形,所以你会得到一个编译器错误” - 你会不会得到一个编译器错误在那种情况下(甚至在 2 年前从未这样做过)。澄清这一点,我会收回我的反对票。
  • 一个被接受的答案可能意味着用户尝试了它并且它对他们有用。他们可能明白我的回答有其局限性,其目的是防止有人意外得出结论,而不是完全阻止变通办法。
【解决方案2】:

一个有趣的问题。我找到了适用于 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;
}

【讨论】:

  • 我认为用户想要的是编译时错误而不是运行时断言。
【解决方案3】:

如果您现在正在使用一些较新的 C++ 变体(不确定 std::is_base_of 需要什么),这可能会对您有所帮助:

我遇到了类似的问题,但我有幸通过一个公共工厂构造所有可能的派生类实例,该工厂分发指向所有可能派生类共享的派生类的其他基类的指针。

所以我在实际派生类型上模板化了这种类型的工厂函数。

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);
}

这是因为我的所有类都具有相同的构造函数参数,即 json 类型,为此我使用 N. Lohmann 的库,但还有其他的。

这不是一种严格禁止在任何地方同时派生 Base1 和 Base2 的方法,但对于大多数用例来说,仅通过这个工厂函数来禁止它可能已经足够了。

【讨论】:

    猜你喜欢
    • 2015-08-09
    • 1970-01-01
    • 1970-01-01
    • 2020-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-05
    • 1970-01-01
    相关资源
    最近更新 更多