双钻到达远方基地class的方法是什么?

What is the way to reach a field or a method of a far away base class in a double diamond?

在 "classical" 钻石问题如下 ([=15= 前面没有 virtual 的那个, 在 class Cclass 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;
}

现在考虑像这样的双钻石:

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

究竟怎样才能达到继承自 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; 

但是(使用 g++)它们都会产生 'X' is an ambiguous base of 'A'..

形式的错误

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

让我们从您收到的错误开始。 'X' is an ambiguous base of 'A'.

编译器告诉您在获取您为 std::cout 选择的对象的父级时有一个 ambiguity/uncertainty。没有指定抽象基础类,你的继承结构是正确的。

/* 
 *   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
 */

现在问题实际上出在您要写入标准输出的内容中。 这是你写的:

cout << B::D::d;
cout << B::D::E::e; 
cout << B::D::F::e;  // When does F inherit from E and get member variable e?
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::e; // When does F inherit from E???
cout << C::D::E::G::g; 
cout << C::D::F::G::g; 

我还没有用 g++ 测试这个(我使用的是 MSVC++),但是这个有效:

A::A() {
    std::cout << B::b
              << B::D::d
              << B::D::E::e
              << B::D::F::f
              << B::D::E::G::g << std::endl;
    /* How do I reach any of the two e's or f's
               or any of the four g's?*/
}

干杯!

编辑: 这是您在 MSVC++ 中遇到的错误

这是它在 MSVC++ 中工作的一些证明”:

现在在 g++ 中:

#include <iostream>
#include <stdio.h>
using namespace std;
class G                      { public: char g = 'G';};
class E : virtual public G           { public: char e = 'E';};
class F : virtual public G           { public: char f = 'F';};
class D : public E, public F { public: char d = 'D';};
class C : virtual public D           { public: char c = 'C';};
class B : virtual public D           { public: char b = 'B';};
class A : public B, public C { public: char a = 'A'; A();};

A::A() {
    std::cout << A::a
              << A::B::b
              << A::B::D::d
              << A::B::D::E::e
              << A::B::D::F::f
              << A::B::D::E::G::g 
              << std::endl;
}

int main() {
    A a;
    cout << endl;
    return 0;
}

这应该有效。

这不起作用的原因:

cout << B::D::d;

是因为:作用域解析是左右关联的;这在某种意义上类似于 (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;

在 C 风格的语法中,您可以使用 ((G&)(E&)(D&)(B&)(*this)).g,尽管这很危险,因为如果您在 class 顺序中犯了错误,您将得到 reinterpret_cast 而不是 static_cast 可能会出现故障。

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

cout << static_cast<G&>(static_cast<E&>(static_cast<B&>(*this))).g;

甚至:

cout << static_cast<B&>(*this).E::g;

因为一旦我们在 B.

,就有一个唯一的 E::g

也可以使用 dynamic_cast 而不是 static_cast,我愿意接受关于哪种样式更好的评论:)