Os especificadores de exceção foram descontinuados porque os especificadores de exceção geralmente são uma péssima ideia . noexcept
foi adicionado porque é o único uso razoavelmente útil de um especificador de exceção: saber quando uma função não lançará uma exceção. Assim, torna-se uma escolha binária: funções que vão lançar e funções que não vão lançar.
noexcept
foi adicionado em vez de apenas remover todos os especificadores de lançamento, exceto throw()
porque noexcept
é mais poderoso. noexcept
pode ter um parâmetro que em tempo de compilação se transforma em um booleano. Se o booleano for verdadeiro, o valor permanece noexcept
. Se o booleano for falso, o noexcept
não pega e a função pode ser lançada.
Assim, você pode fazer algo assim:
struct<typename T>
{
void CreateOtherClass() { T t{}; }
};
Faz CreateOtherClass
exceções lance? Pode, se T
o construtor padrão puder. Como podemos saber? Como isso:
struct<typename T>
{
void CreateOtherClass() noexcept(is_nothrow_default_constructible<T>::value) { T t{}; }
};
Assim, CreateOtherClass()
irá lançar iff o construtor padrão de determinado tipo lançar. Isso corrige um dos principais problemas com especificadores de exceção: sua incapacidade de propagar a pilha de chamadas.
Você não pode fazer isso com throw()
.
noexcept
podem ocorrer verificações de tempo de execução. A principal diferença entre eles é que a quebranoexcept
causastd::terminate
enquanto a quebrathrow
causastd::unexpected
. Também um comportamento de desenrolamento de pilha ligeiramente diferente nesses casos.