try son que las instrucciones example catch c++ exception-specification

c++ - son - ¿Cómo puede std:: runtime_error:: runtime_error(const std:: string &) cumplir con el requisito std:: exception de throw()?



try catch c++ (2)

std::exception requiere que su constructor sea throw() . Sin embargo, std::runtime_error acepta un std::string como su argumento, lo que indica que está almacenando un std::string algún lugar. Por lo tanto, una asignación o copia de construcción tiene que estar en algún lugar. Y para std::string , esa no es una operación de no nothrow .

Entonces, ¿cómo se cumple runtime_error::runtime_error throw() ?

(Para el contexto, estoy implementando un tipo de excepción y quiero almacenar algunas std::string s desde el sitio de la llamada, y quiero hacerlo correctamente ...)


Actualización, 2015:

Sin embargo, std::runtime_error acepta un std::string como su argumento, lo que indica que está almacenando un std::string algún lugar. Por lo tanto, una asignación o copia de construcción tiene que estar en algún lugar. Y para std::string , eso no es una operación noexcept .

runtime_error (y logic_error ) solo están obligados a aceptar un argumento de tipo std::string const & . No están obligados a copiarlo.

Utilice estas sobrecargas a su propio riesgo. LLVM libc ++ no proporciona almacenamiento.

Por otro lado, GNU libstdc ++ se pone de puntillas con cuidado para evitar quedarse sin memoria. Copia el contenido de la cadena, pero en el espacio de almacenamiento de excepción, no en una nueva std::string .

Incluso entonces, agrega una sobrecarga de std::string&& y usa el envío friend para adoptar el búfer interno de un argumento de std::string pasado por rvalue, para conservar también el espacio de almacenamiento de excepciones.

Así que esa es su verdadera respuesta: "Con mucho cuidado, si es que lo hace".

Puede aprovechar la generosidad de GCC utilizando std::runtime_error s como miembros de su propia clase de excepción, almacenando una cadena cada una. Sin embargo, esto aún sería inútil en Clang.


Respuesta original, 2011. Esto sigue siendo cierto:

Una excepción durante el desenrollado de la pila hace que se cancele la terminate .

Pero construir el objeto a lanzar no es parte del desenrollamiento, y no se trata de manera diferente del código antes de la expresión de throw .

Si std::runtime_error::runtime_error( std::string const & ) lanza std::bad_alloc , la excepción runtime_error se pierde (nunca existió) y en su bad_alloc se maneja el bad_alloc .

Demostración: http://ideone.com/QYPj3

En cuanto a su propia clase que almacena std::string s desde el sitio de la llamada, querrá seguir §18.8.1 / 2:

Cada clase de biblioteca estándar T que se deriva de la excepción de clase tendrá un constructor de copia de acceso público y un operador de asignación de copia de acceso público que no salga con una excepción.

Esto es necesario porque la copia de la pila al almacenamiento de excepciones del subproceso es sensible a las excepciones. §15.1 / 7:

Si el mecanismo de manejo de excepciones, después de completar la evaluación de la expresión que se va a lanzar pero antes de que se detecte la excepción, llama a una función que sale a través de una excepción, se llama a std :: terminate (15.5.1).

Por lo tanto, debe usar shared_ptr< std::string > o algo shared_ptr< std::string > para sanear las copias después de la primera.


(Esto Here''s lo mismo en un testcase mínimo).

runtime_error::runtime_error(string const&) no necesita cumplir con throw() .

No hereda ni anula la exception::exception() , y cuando se invoca el constructor de copia de la string , se ha completado la exception::exception() .

Si al copiar la string se lanzara una excepción, se desenrollaría runtime_error::runtime_error(string const&) y luego, supongo, invocar la exception::~exception() .

Es difícil mostrar directamente que no hay un requisito de un ctor derivado para cumplir con el especificador de excepción de un ctor base, pero está fuertemente implícito en el siguiente pasaje (que describe cómo se invoca el destructor de la base, en lugar de pasar la excepción al constructor base) ):

[2003: 15.2/2] Un objeto que está parcialmente construido o parcialmente destruido tendrá destructores ejecutados para todos sus subobjetos totalmente construidos, es decir, para subobjetos para los cuales el constructor ha completado la ejecución y el destructor aún no ha comenzado a ejecutarse. Si un constructor de un elemento de una matriz automática lanza una excepción, solo se destruirán los elementos construidos de esa matriz. Si el objeto o la matriz se asignó en una nueva expresión, se llama a la función de desasignación correspondiente (3.7.3.2, 5.3.4, 12.5), si existe, para liberar el almacenamiento ocupado por el objeto.

El único pasaje que se acerca incluso al escenario que usted presumió (y que presumí inicialmente) es el siguiente.

[2003: 15.4/3] Si una función virtual tiene una especificación de excepción, todas las declaraciones, incluida la definición, de cualquier función que invalide esa función virtual en cualquier clase derivada solo permitirán excepciones permitidas por la especificación de excepción de Función virtual de clase base.

Pero claramente la exception::exception() no es una función virtual, y claramente runtime_error::runtime_error(string const&) no la reemplaza.

(Tenga en cuenta que este escenario se aplicaría a un destructor virtual; en consecuencia, puede ver que, en libstdc ++, runtime_error::~runtime_error() es throw() ).