在双菱形中到达远处基类的一个字段或方法的方法是什么?

iha*_*ato 5 c++ g++ multiple-inheritance

在像下面这样的“经典”菱形问题中(virtual前面public D、后面class C和没有没有class B),可以使用名称范围运算符来解决歧义::,例如在 class 的构造函数中A

/* 
 *   D                            D   D
 *  / \   which without 'virtual' |   |
 * B   C      is actually:        B   C
 *  \ /                            \ /
 *   A                              A
 */
#include <iostream>
using namespace std;
class D                      { public: char d = 'D';};
class C : public D           { public: char c = 'C';};
class B : public D           { public: char b = 'B';};
class A : public B, public C { public: char a = 'A'; A();};

A::A() {
    cout << B::d; //This works! B's d, inherited from D.
    cout << C::d; //This works! C's d, inherited from D.
    //cout << D::d;     //This doesn't work (ambiguous)
    //cout << B::D::d;  //Doesn't work either though.
    //cout << C::D::d;  //Doesn't work either though.
}

int main() {
    A a;
    cout << endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

现在考虑这样的双钻石:

/* 
 *   G                          G  G G  G
 *  / \                         |  | |  |
 * E   F                        E  F E  F
 *  \ /                          \ / \ /
 *   D                            D   D
 *  / \   which without 'virtual' |   |
 * B   C      is actually:        B   C
 *  \ /                            \ /
 *   A                              A
 */
#include <iostream>
using namespace std;
class G                      { public: char g = 'G';};
class E : public G           { public: char e = 'E';};
class F : public G           { public: char f = 'F';};
class D : public E, public F { public: char d = 'D';};
class C : public D           { public: char c = 'C';};
class B : public D           { public: char b = 'B';};
class A : public B, public C { public: char a = 'A'; A();};

A::A() {
    cout << /* How do I reach any of the two e's or f's 
               or any of the four g's?*/
}

int main() {
    A a;
    cout << endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

究竟如何到达从 E、F 和 G 继承的字段?实际上对我来说最合乎逻辑的是以下内容。

cout << B::D::d;
cout << B::D::E::e; 
cout << B::D::F::f; 
cout << B::D::E::G::g; 
cout << B::D::F::G::g; 

cout << C::D::d;
cout << C::D::E::e; 
cout << C::D::F::f; 
cout << C::D::E::G::g; 
cout << C::D::F::G::g; 
Run Code Online (Sandbox Code Playgroud)

但是(使用 g++)它们都会产生形式为 的错误'X' is an ambiguous base of 'A'.

有人可以解释为什么这不起作用以及这样做的正确方法是什么?我错过了什么?

M.M*_*M.M 3

这不起作用的原因:

cout << B::D::d;
Run Code Online (Sandbox Code Playgroud)

是因为:作用域解析是左右关联的;这在某种意义上类似于(B::D) :: d,尽管这里实际上不允许使用括号。这样限定查找B::D就解决了,找到了类型D。只有一种类型称为D ,没有单独的类型A::DB::D。同一类型可以在多个范围中找到。

因此,您得到的等价物D::d是不明确的,因为有多个路径到达类型的基础D


要获得所需的变量,您可能必须使用一系列强制转换,例如:

cout << static_cast<G&>(static_cast<E&>(static_cast<D&>(static_cast<B&>(*this)))).g;
Run Code Online (Sandbox Code Playgroud)

在 C 风格语法中,您可以使用((G&)(E&)(D&)(B&)(*this)).g,尽管这很危险,就像您在类排序中犯了错误一样,您将得到 areinterpret_cast而不是 a static_cast,这可能会发生故障。

实际上,在这种情况下,您可以省略该D步骤,因为 aB具有唯一的E基数:

cout << static_cast<G&>(static_cast<E&>(static_cast<B&>(*this))).g;
Run Code Online (Sandbox Code Playgroud)

甚至:

cout << static_cast<B&>(*this).E::g;
Run Code Online (Sandbox Code Playgroud)

E::g因为一旦我们到达,就有一个独特的B


也可以使用dynamic_cast代替static_cast,我愿意接受关于哪种风格更好的评论:)