Несколько экземпляров подобъекта виртуального базового класса (действительно) - нет? - PullRequest
1 голос
/ 25 января 2012

С учетом кода:

#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 ]

1 голос
/ 03 августа 2012

Ближайшая вещь (не красивая):

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) {}
};
1 голос
/ 25 января 2012

Как правило, все виртуальные базовые классы одного типа объединяются (однако, не виртуальные базовые классы не объединяются с виртуальными).Там нет механизма, чтобы заблокировать виртуальный базовый класс от общего доступа.Вероятно, причина в том, что любой такой механизм потребовал бы довольно больших усилий для разработки (а также усилий для реализации создателей компиляторов) с очень небольшим выигрышем (вы когда-нибудь оказывались в ситуации, когда вы действительно желали получить такую ​​функциональность?)

1 голос
/ 25 января 2012

Вы можете достичь какого-то сравнения с помощью двойной отправки.Это не идеально.Это можно сделать и с меньшим количеством кода, но я просто хотел показать идею, лежащую в основе.

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
1 голос
/ 25 января 2012

Как только база объявлена ​​виртуальной, все источники этой виртуальной базы будут объединены в одну базу этого типа, это не позволит вам разделить ее на половину иерархии (вы ничего не можете сказать у ребёнка де-виртуал родитель). Просто чтобы прояснить, наследование от другого класса, который не наследовал базу, привело бы к другому экземпляру. Вы можете использовать композицию в XYZ для создания двух экземпляров вместо наследования, а затем использовать обычный интерфейс для делегирования в зависимости от ситуации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...