Diferença entre o especificador throw () do C ++ 03 C ++ 11 noexcept

100

Existe alguma diferença entre throw() e noexceptdiferente de ser verificado em tempo de execução e tempo de compilação, respectivamente?

Este artigo da Wikipedia C ++ 11 sugere que os especificadores de lançamento do C ++ 03 estão obsoletos.
Por que então, é noexceptcapaz de cobrir tudo isso em tempo de compilação?

[Observação: verifiquei esta pergunta e este artigo , mas não consegui determinar o motivo sólido para a suspensão de uso.]

iammilind
fonte
7
De acordo com este belo artigo, também noexceptpodem ocorrer verificações de tempo de execução. A principal diferença entre eles é que a quebra noexceptcausa std::terminateenquanto a quebra throwcausa std::unexpected. Também um comportamento de desenrolamento de pilha ligeiramente diferente nesses casos.
Fiktik de
Não há nada "tempo de compilação" verificado com algumas especificações de exceção que é "tempo de execução" verificado em outras. É apenas um mito criado por oponentes das especificações de exceção do C ++.
curioso

Respostas:

129

Os especificadores de exceção foram descontinuados porque os especificadores de exceção geralmente são uma péssima ideia . noexceptfoi 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.

noexceptfoi adicionado em vez de apenas remover todos os especificadores de lançamento, exceto throw()porque noexcepté mais poderoso. noexceptpode 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 noexceptnão pega e a função pode ser lançada.

Assim, você pode fazer algo assim:

struct<typename T>
{
  void CreateOtherClass() { T t{}; }
};

Faz CreateOtherClassexceções lance? Pode, se To 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().

Nicol Bolas
fonte
+1 resposta útil, pelo menos para mim. Ainda estou procurando uma resposta que diga por que eu gostaria de usar noexcept. Eu nunca usei o throw()especificador, nunca e estou tentando determinar se noexceptrealmente fornece algum benefício (além da documentação verificada pelo compilador).
hmjd de
Acabei de encontrar este stackoverflow.com/questions/10787766/… ...
hmjd
1
@NicolBolas concorda. mas se noexcept fosse uma garantia, o compilador poderia verificar se uma função pode lançar ou não em um destruidor. Assim, sendo capaz de avisar um programador que uma função não é exceto ou não.
Alex
2
@NicolBolas as chamadas de tempo de execução std::terminate. o que é MUITO PIOR ! o código pode entrar furtivamente em versões que têm funções marcadas noexcept e violações em tempo de execução (ou seja, em sites de clientes) são detectadas. Eu quis dizer que o compilador garante a geração de código que não lança exceções em primeiro lugar.
Alex
2
@NicolBolas: Outra diferença digna de nota. Se uma função for marcada throws(), se uma exceção for lançada, a pilha deve ser desfeita até o escopo dessa função (para que todas as variáveis ​​automáticas na função sejam destruídas) em que ponto terminate()é chamado (via unexpected()). Se uma função for marcada noexcept, se uma exceção for lançada, terminate será chamado (o desenrolamento da pilha é um detalhe definido pela implementação).
Martin York
33

noexcept não é verificado em tempo de compilação.

Uma implementação não deve rejeitar uma expressão simplesmente porque, quando executada, ela lança ou pode lançar uma exceção que a função contida não permite.

Quando uma função que é declarada noexcept ou throw()tenta lançar uma exceção, a única diferença é que uma chama terminatee as outras chamadas unexpectede o último estilo de tratamento de exceção foi efetivamente descontinuado.

CB Bailey
fonte
Mas se uma função virtual tem throw()/ noexcept, a verificação do tempo de compilação garante que um overrider também tenha.
curioso,
2

std::unexpected() é chamado pelo tempo de execução C ++ quando uma especificação de exceção dinâmica é violada: uma exceção é lançada de uma função cuja especificação de exceção proíbe exceções desse tipo.

std::unexpected() também pode ser chamado diretamente do programa.

Em ambos os casos, std::unexpectedchama o atualmente instalado std::unexpected_handler. As std::unexpected_handlerchamadas padrão std::terminate.

ma13
fonte