1

Given the code:

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

can we make the second assert fail?

In other words, this is the case when we have a two-diamond inheritance graph and would like to have separate subobjects for both diamonds' tops in the most derived object.

The standard (2003, 10.1.4) wording seems to prohibit this, and if really so, the follow-up question is: are we given no means of precise virtual-over-multiply-included subobject structure manipulation, and why?

4

4 に答える 4

1

ベースが仮想であると宣言されると、その仮想ベースのすべてのソースがそのタイプの 1 つのベースに折りたたまれます。階層の途中で分割することはできません (親を非仮想化するために子に言えることは何もありません)。 )。明確にするために、ベースを非仮想的に継承した別のクラスから継承すると、別のインスタンスが生成されます。XYZ継承の代わりにコンポジションを使用して 2 つのインスタンスを作成し、通常のインターフェイスを使用して必要に応じて委任することができます。

于 2012-01-24T20:51:55.167 に答える
1

基本的な規則として、同じタイプのすべての仮想基底クラスがマージされます (ただし、非仮想基底クラスは仮想基底クラスとマージされません)。仮想基本クラスの共有をブロックするメカニズムはありません。その理由はおそらく、そのようなメカニズムは設計にかなりの労力 (およびコンパイラの作成者が実装するための労力) を必要としていたため、利益はほとんどなかったからです (実際にその機能を望んでいた状況に陥ったことはありますか?)。

于 2012-01-24T21:39:38.237 に答える
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
于 2012-01-24T21:55:22.360 に答える
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) {}
};
于 2012-08-03T06:38:12.483 に答える