多重继承的钻石问题:Gcc / Clang 错误但 Visual Studio 中没有错误

A Diamond issue with Multiple Inheritance: Error in Gcc / Clang but no error in Visual Studio

我有以下多重继承结构,其中有一个空的基础和两个空的派生 classes(都代表不同的功能,但这里都省略了)。我有另一个 class 需要这两个功能,因此从两个接口继承。下面的设置使用 visual studio 2015 可以正常编译,但不能使用 gcc 6 或 clang3.8。 gcc 抱怨 Base 是 DerivedC

的模棱两可的基础 class

gcc 错误:

error: ‘Base’ is an ambiguous base of ‘DerivedC’
std::cout << "derivedC.typeId()" << dc.typeId() << std::endl;

Clang 错误:

ambiguous conversion from derived class 'DerivedC' to base class    'const Base':
class DerivedC -> class DerivedA -> class Base
class DerivedC -> class DerivedB -> class Base
    std::cout << "derivedC.typeId()" << dc.typeId() << std::endl;

在使用这些 classes 时,我需要将(指向)Base 模型转换为(指向)DerivedC 模型。如果我想让多重继承成为虚拟的,我需要做一个我想避免的 dynamic_cast 。有什么办法可以让 gcc 和 clang 了解我想要钻石的哪个方向?在我看来,"using DerivedA::typeId" 处理这个(就像它在 VS 中所做的那样(如果缺少 using,它会抱怨))。有趣的是,当您将鼠标悬停在代码上时,VS 会出现 "indicate a ambiguous base error" 问题。但是,它会编译它并 returns 所需的值。

非常感谢任何帮助。 迈克

代码:

#include <iostream>

class Base
{
public:

int typeId() const {
    return doTypeId();
}
private:
virtual int doTypeId() const = 0;
};

class DerivedA : public Base
{
public:

private:
virtual int doTypeId() const override { return 1; }
};

class DerivedB : public Base
{
public:

private:
virtual int doTypeId() const override { return 2; }
};

class DerivedC : public DerivedA, public DerivedB
{
public:
using DerivedA::typeId;
private:
virtual int doTypeId() const override { return 3; }
};

int main(void) {
  auto da = DerivedA();
  std::cout << "DerivedA.typeId() = " << da.typeId() << std::endl;
  auto db = DerivedB();
  std::cout << "DerivedB.typeId() = " << db.typeId() << std::endl;
  auto dc =  DerivedC();
  std::cout << "derivedC.typeId() = " << dc.typeId() << std::endl;
}

因为你没有使用虚继承,所以没有钻石。您有两个 Base

实例
         --------         --------
         | Base |         | Base |
         |  #1  |         |  #2  |
         --------         --------
            V                V
         ------------    ------------
         | DerivedA |    | DerivedB |
         ------------    ------------
                 V         V 
                ------------ 
                | DerivedC | 
                ------------ 

这意味着 DerivedC 的 v-table 有两个用于虚拟方法 doTypeId() 的插槽。 Derived C 中 doTypeId() 的定义覆盖了哪一个? [这不是这里的实际问题,但如果你解决了这个问题,你将不得不面对]

'using' 语句没有解决问题。它将方法的声明从 DerivedA class 提升到 using 出现的上下文中。如果 DerivedB 中方法的签名不同,它将被 using 隐藏,但由于相同,它仍然可用。

名称未在 DerivedC 中声明,但编译器不会跟踪名称的来源。因此,调用仍然是模棱两可的。为了调用在 Base 中声明的成员,它必须知道要传递什么作为 this 指针,但它不知道。

底线:使用虚拟继承。