2012-03-09 10 views
19

Tres compiladores diferentes muestran tres comportamientos diferentes compilar este código:estándar Lo que el C++ dice acerca de perder especificador de tiro en el destructor por defecto

class MyException : public std::exception 
{ 
public: 
    MyException(std::string str) : m_str(str) {} 
    virtual const char * what() const throw() {return m_str.c_str(); } 
protected: 
    std::string m_str; 
}; 

Sun C++ 5.8 Parche 121017-22 2010/09/29: advertencia Función MiExcepción :: ~ MiExcepción() puede lanzar sólo las excepciones producidas por la función std :: excepción :: ~ excepción() se anula

g ++ 3.4.3: error más flojo especificador de tiro para `MiExcepción virtuales :: ~ MyException() '

Visual Studio 2005: Es muy feliz (ni error o advertencia)

class exception { 
public: 
    exception() throw(); 
    exception (const exception&) throw(); 
    exception& operator= (const exception&) throw(); 
    virtual ~exception() throw(); 
    virtual const char* what() const throw(); 
} 

Yo sé cuál es el problema y cómo puedo solucionarlo:

class MyException : public std::exception 
{ 
public: 
    MyException(std::string str) : m_str(str) {} 
    virtual const char * what() const throw() {return m_str.c_str(); } 
    ~MyException() throw() {} <------------ now it is fine! 
protected: 
    std::string m_str; 
}; 

Sin embargo, estoy preguntándose qué dice el estándar en una situación específica.

Me corrió otra pequeña prueba en Visual Studio 2005 y he encontrado algo que me sorprende:

struct Base 
{ 
    virtual int foo() const throw() { return 5; } 
}; 

struct Derived : public Base 
{ 
    int foo() const { return 6; } 
}; 

int main() 
{ 
    Base* b = new Derived; 
    std::cout << b->foo() << std::endl; //<-- this line print 6!!! 
    delete b; 
} 

La firma de las dos funciones son diferentes. ¿Cómo puede esto funcionar? Parece que Visual Studio 2005 ignora por completo la especificación de excepción.

struct Base 
{ 
    virtual int foo() const throw() { return 5; } 
}; 

struct Derived : public Base 
{ 
    int foo() { return 6; } // I have removed the const keyword 
          // and the signature has changed 
}; 

int main() 
{ 
    Base* b = new Derived; 
    std::cout << b->foo() << std::endl; // <-- this line print 5 
    delete b; 
} 

¿Es este estándar de C++? ¿Hay alguna bandera mágica para establecer?

¿Qué hay de VS2008 y VS2010?

+1

virtual int foo() const throw() y int foo() const ambos tienen la misma firma, creo - virtual y throw no cuentan. – JTeagle

+2

Para ser honesto, las especificaciones de excepción generalmente se consideran una mala idea. No ganan nada, y si se lanza una excepción que no figura en la lista, todo el programa se cuelga. – 111111

+1

gcc 3.4 y vs2k5? Si se trata de estándares compatibles, debe usar al menos gcc 4.5 y vs2010 para probar cualquier cosa – PlasmaHH

Respuesta

6

Su programa está mal formado según el estándar C++ y por lo tanto demuestra un comportamiento que no se puede explicar dentro de los reinos del estándar C++.

Referencia:
C++ 03 estándar:

15.4 especificaciones de excepción [except.spec]

Si una función virtual tiene una excepción de especificación, todas las declaraciones, incluyendo el la definición de cualquier función que anule esa función virtual en cualquier clase derivada solo permitirá las excepciones permitidas por la especificación de excepción de la función virtual de la clase base.

[Ejemplo:

struct B 
{ 
    virtual void f() throw (int, double); 
    virtual void g(); 
}; 
struct D: B 
{ 
    void f(); // ill-formed 
    void g() throw (int); // OK 
}; 

La declaración de D::f está mal formada-porque permite que todas las excepciones, mientras que B::f permite solamente int y double.]

+0

¿Qué pasa con el destructor predeterminado? ¿Tiene implícitamente una especificación de "se puede lanzar cualquier" a menos que se anule explícitamente? –

+0

@MarkB: Como se menciona en la especificación estándar anterior para una función de miembro, creo que se aplicaría para un destructor predeterminado implícito y tendrá una excepción "se puede lanzar cualquier". De hecho, una pequeña búsqueda me dice que lo mismo se menciona explícitamente en '# 15.4.13'. –

+1

@Als: en realidad no. Creo que fue el caso en C++ 03 pero en C++ 11 para el método generado implícitamente, el compilador tiene la tarea de generar la especificación de excepción más estricta que puede (derivada de las de los atributos). –

5

Se desarrolló un poco en C++ 11 [except.spec]:

5/ Si una función virtual tiene una excepción de especificación, todas las declaraciones, incluida la definición , de cualquier función que anule esa función virtual en cualquier clase derivada solo permitirá excepciones permitidas por la especificación de excepción de la función virtual de la clase base.

por lo que son en realidad nunca permite especificar una especificación más flojo excepción.

Sin embargo, este caso es complicado porque el destructor realmente está sintetizado por el propio compilador.

En C++ 03, creo que la norma no fue tan cuidadoso con ellos, y que tenía que escribirlos usted mismo, en C++ 11 Sin embargo, obtenemos:

14/ Un implícitamente la función de miembro especial declarada (Cláusula 12) tendrá una especificación de excepción. Si f es un constructor predeterminado declarado implícitamente, constructor de copia, mover constructor, destructor, operador de asignación de copia o operador de asignación de movimiento, su especificación de excepción implícita especifica el tipo-id T si y solo si T está permitido por la especificación de excepción de una función invocada directamente por la definición implícita f; f permitirá todas las excepciones si cualquier función que invoca directamente permite todas las excepciones, y f no permitirá excepciones si cada función que invoca directamente no permite excepciones.

Cuando el compilador generará la especificación excepción del destructor para que coincida con lo que puede ser lanzada desde las funciones que llama (es decir, los destructores de los atributos). Si esos destructores no arrojan, generará un destructor noexcept que satisfará la restricción de clase base.

Nota: VS2005 es uno de los compiladores menos compatibles con el estándar que pueda encontrar en la Tierra.

+0

Lo sé, pero es el uno al que podría acceder en este momento :-( –

+0

¿Deberíamos interpretar que esto significa que una clase derivada y una clase base -ninguna de las cuales especifica lo que arrojan- generará el error "especificador de tiro más flexible" en los casos en que la clase derivada llamar a las funciones incorrectas? – sage

+0

@sage: no estoy seguro de lo que está preguntando. Si escribe una función y no especifica una especificación de excepción, se considera que esta función puede arrojar * cualquier cosa * y, por lo tanto, no puede modificarse más flojo. –

Cuestiones relacionadas