It seems that my problem is a bug in MSVC. I’m using the Visual Studio 2008 with Service Pack 1, and my code works with GCC (as tested on codepad.org).
Any official info on this bug? Any ideas how to work around it? Is the bug fixed in VS2010? All insights would be greatly appreciated.
The code:
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;
}
It looks as though the compiler is not correctly adjusting the
thispointer when calling throughA::clone. If you remove the declaration ofA::clonethen everything works fine.Digging in deeper, when you have
A::clone, the vtable looks like this:And foo calls
elem.__vfptr[2], offsettingthisincorrectly by -4 bytes. WithoutA::clone, the vtable looks like this:And foo calls
elem.__vfptr[1]. That does not adjustthisat all (and the code assumes thatthiswill be equal toBaseinstead ofB).So it looks like the compiler assumes that
A::cloneis a new virtual method and doesn’t overrideBase::clonewhen determining whetherArequires a new virtual table, but then some other code later determines thatAdoes not need a virtual table. You can verify this by comparingsizeof(B)with or without a new virtual function:So it’s a compiler bug.