Виртуальная ошибка наследования в MSVC

Кажется, что моей проблемой является ошибка в MSVC. Я использую Visual Studio 2008 с Пакетом обновления 1, и мои работы кода с GCC (как протестировано на codepad.org).

Какая-либо официальная информация об этой ошибке? Какие-либо идеи, как работать вокруг этого? Ошибка исправлена в VS2010? Все понимание значительно ценилось бы.

Код:

struct Base {
    Base(int i = 0) : i(i) {}
    virtual ~Base() {}
    virtual Base *clone() const = 0;

protected:
    int i;
};

struct A : virtual public Base {
    A() {}
    virtual A *clone() const = 0;
};

struct B : public A {
    B() {}
    B *clone() const { return new B(*this); }

    /// MSVC debugger shows that 'b' is for some reason missing the Base
    /// portion of it's object ("Error: expression cannot be evaluated")
    /// and trying to access 'b.i' causes an unhandled exception.
    ///
    /// Note: This only seems to occur with MSVC
    B(const B &b) : Base(b.i), A() {}
};

void foo(const A &elem) {
    A *a = elem.clone();
    if (a) delete a;
}

int main() {
    A *a = new B;
    foo(*a);
    delete a;
}

14
задан Alexander Dunaev 24 October 2012 в 05:31
поделиться

2 ответа

Похоже, что компилятор неправильно настраивает указатель this при вызове через A :: clone . Если вы удалите объявление A :: clone , все будет работать нормально.

Если копнуть глубже, когда у вас есть A :: clone , таблица vtable выглядит так:

    [0x0]   0x002f1136 [thunk]:B::`vector deleting destructor'`vtordisp{4294967292,0}' (unsigned int)   void *
    [0x1]   0x002f11e0 [thunk]:B::clone`vtordisp{4294967292,0}' (void)  void *
    [0x2]   0x002f12ad [thunk]:B::clone`vtordisp{4294967292,4}' (void)  void *
    [0x3]   0x002f12a3 B::clone(void)   void *

И foo вызывает elem .__ vfptr [2] , смещая это неверно на -4 байта. Без A :: clone таблица vtable выглядит так:

    [0x0]   0x00ee1136 [thunk]:B::`vector deleting destructor'`vtordisp{4294967292,0}' (unsigned int)   void *
    [0x1]   0x00ee11e0 [thunk]:B::clone`vtordisp{4294967292,0}' (void)  void *
    [0x2]   0x00ee12a3 B::clone(void)   void *

И foo вызывает elem .__ vfptr [1] . Это вообще не регулирует this (и код предполагает, что this будет равно Base вместо B )).

Таким образом, похоже, что компилятор предполагает, что A :: clone является новым виртуальным методом и не переопределяет Base :: clone при определении того, A ] требует новую виртуальную таблицу, но позже какой-то другой код определяет, что A не нуждается в виртуальной таблице. Вы можете убедиться в этом, сравнив sizeof (B) с новой виртуальной функцией или без нее:

struct A : virtual public Base {
    A() {}
    virtual A *clone() const = 0;
}; //sizeof(B)==16

struct A : virtual public Base {
    A() {}
    virtual A *clone() const = 0;
virtual const A *clone2() const { return this; }
}; //sizeof(B)==20

Так что это ошибка компилятора.

8
ответ дан 1 December 2019 в 15:21
поделиться

На основе SoC я думаю, что вы должны вернуть ошибки из ваших служб и объединить их в вашем ModelState, если это необходимо.

Но наша цель - поддерживать развязку, а также использовать метод ModelState.Merge (). Это не так?

Существует конкретная реализация , которая может помочь

-121--2731911-

Выглядит так, будто компилятор неправильно настраивает этот указатель при вызове через A:: clone . Если удалить объявление A:: clone , то все работает нормально.

Копаясь глубже, когда у вас есть A:: clone , vtable выглядит так:

    [0x0]   0x002f1136 [thunk]:B::`vector deleting destructor'`vtordisp{4294967292,0}' (unsigned int)   void *
    [0x1]   0x002f11e0 [thunk]:B::clone`vtordisp{4294967292,0}' (void)  void *
    [0x2]   0x002f12ad [thunk]:B::clone`vtordisp{4294967292,4}' (void)  void *
    [0x3]   0x002f12a3 B::clone(void)   void *

И foo вызывает elem. __ vfptr [2] , смещая это неправильно на -4 байта. Без A:: clone vtable выглядит следующим образом:

    [0x0]   0x00ee1136 [thunk]:B::`vector deleting destructor'`vtordisp{4294967292,0}' (unsigned int)   void *
    [0x1]   0x00ee11e0 [thunk]:B::clone`vtordisp{4294967292,0}' (void)  void *
    [0x2]   0x00ee12a3 B::clone(void)   void *

И foo вызывает elem. __ vfptr [1] . При этом это не корректируется (код предполагает, что этот будет равен Base вместо B ).

Таким образом, компилятор предполагает, что A:: clone является новым виртуальным методом и не переопределяет Base:: clone при определении, требует ли A новую виртуальную таблицу, но затем какой-либо другой код позже определяет, что A не нуждается в виртуальной таблице. Вы можете проверить это, сравнивая sizeof (B) с новой виртуальной функцией или без нее:

struct A : virtual public Base {
    A() {}
    virtual A *clone() const = 0;
}; //sizeof(B)==16

struct A : virtual public Base {
    A() {}
    virtual A *clone() const = 0;
virtual const A *clone2() const { return this; }
}; //sizeof(B)==20

Поэтому это ошибка компилятора.

-121--1863520-

Кажется (из некоторых тестов), что ошибка вызвана сочетанием виртуального базового класса с чистым виртуальным методом с использованием ковариантных возвращаемых типов.

Поскольку либо удаление чистого виртуального метода из класса Base, либо превращение Base в невиртуальный базовый класс, либо превращение метода clone () в нековариантный, похоже, устраняет ошибку.

Я думаю, что этот решен для меня (после того, как я отправлю сообщение об ошибке в MS), и у меня даже осталось несколько вариантов, чтобы обойти его.:)

1
ответ дан 1 December 2019 в 15:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: