虚拟基类子对象的多个实例(真的) - 没办法?

时间:2012-01-24 20:44:38

标签: c++ virtual-inheritance diamond-problem

鉴于代码:

#include <cassert>


struct X {};

struct Y1: virtual X {};
struct Y2: virtual X {};
struct Y3: virtual X {};
struct Y4: virtual X {};

struct Z1: Y1, Y2 {};
struct Z2: Y3, Y4 {};

struct XYZ: Z1, Z2 {};


int main() {
    XYZ xyz;

    X *x1 = static_cast<Y1*>(&xyz);
    X *x2 = static_cast<Y2*>(&xyz);
    X *x3 = static_cast<Y3*>(&xyz);
    X *x4 = static_cast<Y4*>(&xyz); 

    assert( x1 == x2 ); //first pair, ok
    assert( x2 == x3 ); //can we make this one fail?
    assert( x3 == x4 ); //second pair, ok


    return 0;
}

我们可以让第二个断言失败吗?

换句话说,当我们有一个双钻石继承图并且想要在最派生的对象中为两个钻石的顶部都有单独的子对象时就是这种情况。

标准(2003年,10.1.4)的措辞似乎禁止这样做,如果真的如此,后续问题是:我们没有给出精确的虚拟过度包含子对象结构操作的方法,以及为什么?

4 个答案:

答案 0 :(得分:1)

一旦将基础声明为虚拟,该虚拟基础的所有源将折叠为该类型的一个基础,它不会让您将其从层次结构的中间分开(没有什么可以在一个孩子说对父母进行去虚拟)。为了清楚起见,从非实际继承基类的不同类继承将导致另一个实例。您可以使用XYZ中的组合来创建两个实例而不是继承,然后根据需要使用普通接口进行委派。

答案 1 :(得分:1)

作为基本规则,合并了相同类型的所有虚拟基类(尽管非虚拟基类不与虚拟基类合并)。没有阻止虚拟基类被共享的机制。原因可能是任何这样的机制都需要花费很多精力来设计(并且编译器编写者也需要付出很少的努力)才能获得很少的收益(你是否曾经遇到过你真正希望获得该功能的情况?)

答案 2 :(得分:1)

您可以通过双重调度实现某种比较。这不完美。这可以用更少的代码来完成,但我只想展示它背后的想法。

class BaseX {
    bool Equals(BaseX* potentialBaseX) {
        if(potentialBaseX) {
            return potentialBaseX->TestEquals(this);
        }
        // handles null
        return false;
    }

    // OK: x to x
    virtual bool TestEquals(BaseX* baseX) { return true; }
    virtual bool TestEquals(DerivedY1* derivedY) { return false; }
    virtual bool TestEquals(DerivedY2* derivedY) { return false; }
    virtual bool TestEquals(DerivedY3* derivedY) { return false; }
    virtual bool TestEquals(DerivedY4* derivedY) { return false; }
};
class DerivedY1 {
    // OK: y1 to y1, y1 to y2
    virtual bool TestEquals(BaseX* baseX) { return false; }
    virtual bool TestEquals(DerivedY1* derivedY) { return true; }
    virtual bool TestEquals(DerivedY2* derivedY) { return true; }
    virtual bool TestEquals(DerivedY3* derivedY) { return false; }
    virtual bool TestEquals(DerivedY4* derivedY) { return false; }
};    
class DerivedY2 {
    // OK: y2 to y2, y2 to y1
    virtual bool TestEquals(BaseX* baseX) { return false; }
    virtual bool TestEquals(DerivedY1* derivedY) { return true; }
    virtual bool TestEquals(DerivedY2* derivedY) { return true; }
    virtual bool TestEquals(DerivedY3* derivedY) { return false; }
    virtual bool TestEquals(DerivedY4* derivedY) { return false; }
};
class DerivedY3 {
    // OK: y3 to y3, y3 to y4
    virtual bool TestEquals(BaseX* baseX) { return false; }
    virtual bool TestEquals(DerivedY1* derivedY) { return false; }
    virtual bool TestEquals(DerivedY2* derivedY) { return false; }
    virtual bool TestEquals(DerivedY3* derivedY) { return true; }
    virtual bool TestEquals(DerivedY4* derivedY) { return true; }
};
class DerivedY4 {
    // OK: y4 to y4, y4 to y3
    virtual bool TestEquals(BaseX* baseX) { return false; }
    virtual bool TestEquals(DerivedY1* derivedY) { return false; }
    virtual bool TestEquals(DerivedY2* derivedY) { return false; }
    virtual bool TestEquals(DerivedY3* derivedY) { return true; }
    virtual bool TestEquals(DerivedY4* derivedY) { return true; }
};

//Using your example:
assert( x1.Equals(x2) ); //first pair, ok
assert( x2.Equals(x3) ); //can we make this one fail?
assert( x3.Equals(x4) ); //second pair, ok

答案 3 :(得分:1)

最近的东西(不漂亮):

struct XYZ1;
struct XYZ2;

struct XYZ1 : Z1 {
    XYZ2 &self;
    XYZ1 (XYZ2 &self) : self(self) {}
};
struct XYZ2 : Z2 {
    XYZ1 &self;
    XYZ2 (XYZ1 &self) : self(self) {}
};
struct XYZ {
    XYZ1 m1;
    XYZ2 m2;
    XYZ() : m1(m2), m2(m1) {}
};