Кажется, что моей проблемой является ошибка в 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;
}
Похоже, что компилятор неправильно настраивает указатель 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
Так что это ошибка компилятора.
На основе 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), и у меня даже осталось несколько вариантов, чтобы обойти его.:)