具有多重继承的Diamond问题:Gcc / Clang中出错但Visual Studio中没有错误

时间:2016-09-01 14:37:41

标签: c++ visual-studio gcc multiple-inheritance

我有以下多重继承结构,其中包含一个空基和两个空的派生类(两者都代表不同的功能,但这都是遗漏的)。我有另一个类需要这两个功能,因此从两个接口继承。下面的设置使用visual studio 2015进行编译,但不能使用gcc 6或clang3.8编译。 gcc抱怨Base是一个模糊的DerivedC基类

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;

在使用这些类时,我需要将(指针)Base模型转换为(指向)DerivedC模型的指针。如果我将虚拟多重继承,我需要做一个我想避免的dynamic_cast。有什么方法可以让gcc和clang了解我想采取的钻石方向?在我看来,“使用DerivedA :: typeId”处理这个问题(就像它在VS中一样(如果缺少使用它会发出抱怨))。有趣的是,当你将鼠标悬停在代码上时,VS确实“表示一个模糊的基本错误”问题。但是,它会编译它并返回所需的值。

任何帮助非常感谢。 迈克

代码:

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

1 个答案:

答案 0 :(得分:0)

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

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

这意味着DerivedC的v表有两个用于虚方法doTypeId()的槽。派生C中的doTypeId()定义覆盖了哪一个? [这不是这里的实际问题,但是如果你解决这个问题就必须应付这个问题]

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

该名称未在DerivedC中声明,但编译器不会跟踪名称的来源。因此,电话仍然含糊不清。为了调用Base中声明的成员,它必须知道要传递的内容为this指针并且它无法分辨。

底线:使用虚拟继承。