¿Cómo construir una excepción <stdexcept> o <system_error> sin lanzar?
Excepciones definidas en<stdexcept>
(p.ej.std::logic_error
, std::runtime_error
y sus subclases comostd::system_error
) tienen constructores que esperan argumentos de cadena, por ejemplo:
domain_error(const string& what_arg);
domain_error(const char* what_arg);
con postcondiciones
strcmp(what(), what_arg.c_str()) == 0
strcmp(what(), what_arg) == 0
respectivamente. No hay ningún requisito de que estos argumentos pasados a los constructores sigan siendo válidos durante la vigencia de estas excepciones, por lo que la única forma de garantizar que se cumplan las condiciones posteriores esduplicar y almacenar estas cadenas dinámicas. Esto requiere memoria, por lo que supongo que su construcción en sí misma puede arrojarstd::bad_alloc
o similar, que suele sermás inesperado. Esto causa problemas, porque cada ejemplo de código que he visto en la naturaleza alienta a las personas a escribir código como
if (haveError)
throw std::runtime_error("BOO!"); // May throw std::bad_alloc instead?!
mientras que sería mucho más seguro construir la excepción de antemano en otro lugar, por ejemplo:
struct A {
// During allocation of A one would often expect std::bad_alloc anyway:
A() : m_someException("BOO!") {}
void f() {
/* Do stuff */
if (haveError)
throw m_someException;
/* Note that according to §18.8.1.2 all standard library
classes deriving from `std::exception` must have publicly
accessible copy constructors and copy assignment operators
that do not exit with an exception. In implementations such
exception instances most likely share the common string
with all their copies. */
}
std::runtime_error const m_someException;
};
Esto me hace muy cauteloso con las bibliotecas que lanzan tales excepciones, por ejemplo, inclusoregex_error
de<regex>
en C ++ 11 !!!
¿Por qué estas excepciones no tienen constructores no-throw / noexcept? ¿Las directrices básicas de C ++ tienen algo que decir sobre esto?
PD: personalmente me hubiera idowhat()
un método abstracto puro en este punto en la cadena de ascendencia de excepción.
EDITAR 09.10.2017: Aquí hay un PoC que demuestra questd::runtime_error
la construcción puede arrojar unstd::bad_alloc
en lugar:
#include <cstddef>
#include <cstdlib>
#include <new>
#include <stdexcept>
#include <string>
bool throwOnAllocate = false;
void * operator new(std::size_t size) {
if (!throwOnAllocate)
if (void * const r = std::malloc(size))
return r;
throw std::bad_alloc();
}
void operator delete(void * ptr) { std::free(ptr); }
int main() {
std::string const errorMessage("OH NOEZ! =(");
throwOnAllocate = true;
throw std::runtime_error(errorMessage);
}