多重继承的钻石问题: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
指针,但它不知道。
底线:使用虚拟继承。
我有以下多重继承结构,其中有一个空的基础和两个空的派生 classes(都代表不同的功能,但这里都省略了)。我有另一个 class 需要这两个功能,因此从两个接口继承。下面的设置使用 visual studio 2015 可以正常编译,但不能使用 gcc 6 或 clang3.8。 gcc 抱怨 Base 是 DerivedC
的模棱两可的基础 classgcc 错误:
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
指针,但它不知道。
底线:使用虚拟继承。