2010-02-10 7 views
14

Parece que mi problema es un error en MSVC. Estoy usando Visual Studio 2008 con Service Pack 1, y mi código funciona con GCC (como se probó en codepad.org).Error de herencia virtual en MSVC

¿Alguna información oficial sobre este error? ¿Alguna idea de cómo solucionarlo? ¿El error está solucionado en VS2010? Todas las ideas serán muy apreciadas.

El código:

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; 
} 
+1

Esto parece ser un error. – GManNickG

+2

Todavía lo hace en Visual Studio 2010. – Corey

+0

Noté que el teclado usa g ++ 4.1.2, así que lo intenté con Borland C++ 5.82 y funciona bien. – Corey

Respuesta

8

Parece como si el compilador no se está ajustando correctamente el puntero this cuando se llama a través de A::clone. Si elimina la declaración de A::clone, todo funciona bien.

de excavación en el más profundo, cuando se tiene A::clone, la viable se ve así:

[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 * 

Y foo llama elem.__vfptr[2], compensando this incorrectamente -4 bytes. Sin A::clone, la viable es el siguiente:

[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 * 

Y foo llama elem.__vfptr[1]. Eso no ajusta this en absoluto (y el código asume que this será igual a Base en lugar de B).

lo que parece que el compilador asume que A::clone es un nuevo método virtual y no anula Base::clone al determinar si A requiere una nueva tabla virtual, pero entonces algún otro código más tarde determina que A no necesita una tabla virtual. Esto se puede comprobar mediante la comparación de sizeof(B) con o sin una nueva función virtual:

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 

Por lo tanto, es un error del compilador.

1

Parece (a partir de algunas pruebas) que el error se debe a la combinación de una clase base virtual con un método virtual puro que utiliza tipos de retorno covariantes.

Dado que eliminar el método virtual puro de la clase Base, o hacer que Base sea una clase base no virtual o hacer que el método clone() no sea covariante parece resolver el error.

Supongo que este ha sido resuelto por mí (después de enviar un informe de error a MS), e incluso me quedan algunas opciones para eludirlo. :)

Cuestiones relacionadas