- Existem muitos exemplos de funções que eu sei que nunca serão lançadas, mas para as quais o compilador não pode determinar isso sozinho. Devo acrescentar noexcept à declaração da função em todos esses casos?
noexcept
é complicado, pois faz parte da interface de funções. Especialmente, se você estiver escrevendo uma biblioteca, o código do seu cliente pode depender da noexcept
propriedade. Pode ser difícil alterá-lo mais tarde, pois você pode quebrar o código existente. Isso pode ser menos preocupante quando você está implementando código que é usado apenas pelo seu aplicativo.
Se você tem uma função que não pode ser noexcept
executada , pergunte-se se ela gostaria de permanecer ou isso restringiria futuras implementações? Por exemplo, você pode querer introduzir uma verificação de erro de argumentos ilegais lançando exceções (por exemplo, para testes de unidade) ou pode depender de outro código de biblioteca que possa alterar sua especificação de exceção. Nesse caso, é mais seguro ser conservador e omitir noexcept
.
Por outro lado, se você tem certeza de que a função nunca deve ser lançada e está correto que faz parte da especificação, você deve declará-la noexcept
. No entanto, lembre-se de que o compilador não poderá detectar violações noexcept
se a sua implementação mudar.
- Para quais situações devo ter mais cuidado com o uso de noexcept e para quais situações posso me safar com o implícito noexcept (false)?
Existem quatro classes de funções nas quais você deve se concentrar porque elas provavelmente terão o maior impacto:
- mover operações (mover operador de atribuição e mover construtores)
- operações de swap
- desalocadores de memória (exclusão do operador, exclusão do operador [])
- destruidores (embora estes sejam implicitamente, a
noexcept(true)
menos que você os faça noexcept(false)
)
Essas funções geralmente devem ser noexcept
e é mais provável que as implementações da biblioteca possam fazer uso da noexcept
propriedade. Por exemplo, std::vector
pode usar operações de movimentação sem arremesso sem sacrificar fortes garantias de exceção. Caso contrário, ele terá que voltar aos elementos de cópia (como no C ++ 98).
Esse tipo de otimização está no nível algorítmico e não depende de otimizações do compilador. Pode ter um impacto significativo, especialmente se os elementos forem caros de copiar.
- Quando posso realisticamente esperar observar uma melhora no desempenho depois de usar o noexcept? Em particular, dê um exemplo de código para o qual um compilador C ++ é capaz de gerar melhor código de máquina após a adição de noexcept.
A vantagem da noexcept
especificação contra nenhuma exceção ou throw()
é que o padrão permite aos compiladores mais liberdade quando se trata de empilhar o desenrolamento. Mesmo no throw()
caso, o compilador precisa desenrolar completamente a pilha (e deve fazê-lo na ordem inversa exata das construções do objeto).
No noexcept
caso, por outro lado, não é necessário fazer isso. Não é necessário que a pilha precise ser desenrolada (mas o compilador ainda pode fazê-lo). Essa liberdade permite uma otimização adicional do código, pois diminui a sobrecarga de ser sempre capaz de relaxar a pilha.
A pergunta relacionada sobre noexcept, desenrolamento de pilha e desempenho entra em mais detalhes sobre a sobrecarga quando é necessário desenrolar a pilha.
Eu também recomendo o livro de Scott Meyers "Effective Modern C ++", "Item 14: Declarar funções como" Exceto se não emitirem exceções "para leitura posterior.
move_if_nothrow
(ou whatchamacallit) verá uma melhoria de desempenho se houver um mecanismo de exceção exceto.