Especificações de exceção (throw, noexcept) (C++)

As especificações de exceção são um recurso de linguagem C++ que indica a intenção do programador sobre os tipos de exceção que podem ser propagados por uma função. Você pode especificar que uma função pode ou não sair por uma exceção usando uma especificação de exceção. O compilador poderá usar essas informações para otimizar chamadas para a função e encerrar o programa se uma exceção inesperada escapar da função.

Antes do C++17, havia dois tipos de especificação de exceção. A especificação noexcept era nova no C++11. Ela especifica se o conjunto de exceções potenciais que podem escapar da função está vazio. A especificação de exceção dinâmica, ou especificação throw(optional_type_list), foi preterida no C++11 e removida no C++17, exceto para throw(), que é um alias para noexcept(true). Esta especificação de exceção foi criada para fornecer informações resumidas sobre exceções que podem ser lançadas fora de uma função, mas na prática foi verificado que eram problemáticas. A especificação de uma exceção dinâmica que provou ser um pouco útil era a especificação throw() incondicional. Por exemplo, a declaração de função:

void MyFunction(int i) throw();

informa o compilador que a função não lança exceções. No entanto, no modo /std:c++14, isso poderá levar a um comportamento indefinido se a função gerar uma exceção. Portanto, recomendamos usar o operador noexcept em vez do acima:

void MyFunction(int i) noexcept;

A tabela a seguir resume a implementação do Microsoft C++ de especificações de exceção:

Especificação de exceção Significado
noexcept
noexcept(true)
throw()
A função não gera uma exceção. No modo /std:c++14 (que é o padrão), noexcept e noexcept(true) são equivalentes. Quando uma exceção é gerada de uma função declarada noexcept ou noexcept(true), std::terminate é invocada. Quando uma exceção é gerada de uma função declarada como throw() no modo /std:c++14, o resultado é um comportamento indefinido. Nenhuma função específica é invocada. Essa é uma divergência do padrão C++14, que exigiu que o compilador invocasse std::unexpected.
Visual Studio 2017 versão 15.5 e posterior: no modo /std:c++17, noexcept, noexcept(true) e throw() são todos equivalentes. No modo /std:c++17, throw() é um alias para noexcept(true). No modo /std:c++17 e posterior, quando uma exceção é lançada de uma função declarada com qualquer uma dessas especificações, std::terminate é invocada conforme exigido pelo padrão C++17.
noexcept(false)
throw(...)
Nenhuma especificação
A função pode gerar uma exceção de qualquer tipo.
throw(type) (C++14 e anteriores) A função pode gerar uma exceção do tipo type. O compilador aceita a sintaxe, mas a interpreta como noexcept(false). No modo /std:c++17 e posterior, o compilador emite um aviso C5040.

Se o tratamento de exceção for usado em um aplicativo, deverá haver uma função na pilha de chamadas que manipule exceções geradas antes de sair do escopo externo de uma função marcada noexcept, noexcept(true)ou throw(). Se as funções chamadas entre aquela que gera uma exceção e a que manipula a exceção forem especificadas como noexcept, noexcept(true) (ou throw() no modo /std:c++17), o programa será encerrado quando a função noexcept propagar a exceção.

O comportamento de exceção de uma função depende dos fatores a seguir:

  • Qual modo de compilação padrão de linguagem está definido.

  • Se você estiver compilando a função em C ou C++.

  • Qual opção do compilador /EH você usa.

  • Se a especificação de exceção for determinada explicitamente.

As especificações explícitas de exceção não são permitidas em funções C. Uma função C é assumida para não gerar exceções sob /EHsc e pode gerar exceções estruturadas em /EHs, /EHa ou /EHac.

A tabela a seguir resume se uma função C++ pode potencialmente ser lançada sob várias opções de tratamento de exceção do compilador:

Função /EHsc /EHs /EHa /EHac
Função C++ sem especificação de exceção Sim Sim Sim Yes
Função C++ com especificação de exceção noexcept, noexcept(true) ou throw() Não No Sim Yes
Função C++ com especificação de exceção noexcept(false), throw(...) ou throw(type) Sim Sim Sim Yes

Exemplo

// exception_specification.cpp
// compile with: /EHs
#include <stdio.h>

void handler() {
   printf_s("in handler\n");
}

void f1(void) throw(int) {
   printf_s("About to throw 1\n");
   if (1)
      throw 1;
}

void f5(void) throw() {
   try {
      f1();
   }
   catch(...) {
      handler();
    }
}

// invalid, doesn't handle the int exception thrown from f1()
// void f3(void) throw() {
//   f1();
// }

void __declspec(nothrow) f2(void) {
   try {
      f1();
   }
   catch(int) {
      handler();
    }
}

// only valid if compiled without /EHc
// /EHc means assume extern "C" functions don't throw exceptions
extern "C" void f4(void);
void f4(void) {
   f1();
}

int main() {
   f2();

   try {
      f4();
   }
   catch(...) {
      printf_s("Caught exception from f4\n");
   }
   f5();
}
About to throw 1
in handler
About to throw 1
Caught exception from f4
About to throw 1
in handler

Confira também

Instruçõestry, throw e catch (C++)
Práticas recomendadas do C++ modernas para tratamento de erros e exceções