Namespaces
Variants

Dynamic exception specification (until C++17)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous
Exceptions
try block
Throwing exceptions
Handling exceptions
Exception specification
noexcept specification (C++11)
dynamic specification ( until C++17* )
noexcept operator (C++11)

Перечисляет исключения, которые функция может выбрасывать прямо или косвенно.

Содержание

Синтаксис

throw( type-id-list  (optional) ) (1) (deprecated in C++11)
(removed in C++17)
1) Явная спецификация динамических исключений.
type-id-list - список, разделённый запятыми, из type-ids , type-id, представляющий pack expansion , сопровождается многоточием (...) (since C++11)

Явная спецификация динамического исключения должна появляться только в деклараторе функции для типа функции, указателя на тип функции, ссылки на тип функции или указателя на тип функции-члена, который является типом верхнего уровня в объявлении или определении, или в таком типе, появляющемся как параметр или возвращаемый тип в деклараторе функции.

void f() throw(int);            // OK: объявление функции
void (*pf)() throw (int);       // OK: объявление указателя на функцию
void g(void pfa() throw(int));  // OK: объявление параметра-указателя на функцию
typedef int (*pf)() throw(int); // Ошибка: объявление typedef

Объяснение

Если функция объявлена с типом T в её динамической спецификации исключений, функция может выбрасывать исключения этого типа или типа, производного от него.

Неполные типы , указатели или ссылки на неполные типы, кроме cv void* , и типы rvalue-ссылок (начиная с C++11) не допускаются в спецификации исключений. Типы массивов и функций, если используются, преобразуются в соответствующие типы указателей, а квалификаторы верхнего уровня cv также отбрасываются. Параметры пакетов допускаются (начиная с C++11) .

Динамическая спецификация исключений, чей набор скорректированных типов пуст (после раскрытия любых паков) (начиная с C++11) является небросающей. Функция с небросающей динамической спецификацией исключений не позволяет никаких исключений.

Динамическая спецификация исключений не считается частью типа функции.

Если функция выбрасывает исключение типа, не указанного в её спецификации исключений, вызывается функция std::unexpected . Функция по умолчанию вызывает std::terminate , но может быть заменена пользовательской функцией (через std::set_unexpected ), которая может вызывать std::terminate или выбрасывать исключение. Если исключение, выброшенное из std::unexpected , допускается спецификацией исключений, раскрутка стека продолжается как обычно. Если нет, но std::bad_exception разрешён спецификацией исключений, выбрасывается std::bad_exception . В противном случае вызывается std::terminate .

Инстанцирование

Динамическая спецификация исключений специализации шаблона функции не инстанцируется вместе с объявлением функции; она инстанцируется только тогда, когда требуется (как определено ниже).

Динамическая спецификация исключений неявно объявленной специальной функции-члена также вычисляется только когда это необходимо (в частности, неявное объявление функции-члена производного класса не требует инстанцирования спецификации исключений базовой функции-члена).

Когда динамическая спецификация исключений специализации шаблона функции требуется , но еще не была инстанцирована, зависимые имена подвергаются поиску, и любые шаблоны, используемые в expression , инстанцируются как если бы для объявления специализации.

Динамическая спецификация исключений функции считается необходимой в следующих контекстах:

  • в выражении, где функция выбирается с помощью разрешения перегрузки
  • функция является odr-used
  • функция была бы odr-used, но появляется в невычисляемом операнде
template<class T>
T f() throw(std::array<char, sizeof(T)>);
int main()
{
    decltype(f<void>()) *p; // f не вычисляется, но требуется спецификация исключений
                            // ошибка, потому что инстанцирование спецификации исключений
                            // вычисляет sizeof(void)
}
  • спецификация необходима для сравнения с другим объявлением функции (например, при переопределении виртуальной функции или явной специализации шаблона функции)
  • в определении функции
  • спецификация необходима, поскольку специальная функция-член по умолчанию должна проверить её, чтобы определить свою собственную спецификацию исключений (это происходит только тогда, когда спецификация специальной функции-члена по умолчанию сама по себе необходима).

Потенциальные исключения

Каждая функция f , указатель на функцию pf и указатель на функцию-член pmf имеет набор потенциальных исключений , который состоит из типов, которые могут быть выброшены. Набор всех типов указывает, что может быть выброшено любое исключение. Этот набор определяется следующим образом:

1) Если объявление f , pf или pmf использует динамическую спецификацию исключений которая не разрешает все исключения (до C++11) , множество состоит из типов, перечисленных в этой спецификации.
2) В противном случае, если объявление f , pf , или pmf использует noexcept(true) , множество является пустым.
(since C++11)
3) В противном случае, множество представляет собой множество всех типов.

Примечание: для неявно объявленных специальных функций-членов (конструкторов, операторов присваивания и деструкторов) и для наследуемых конструкторов (since C++11) , набор потенциальных исключений представляет собой комбинацию наборов потенциальных исключений всего, что они вызывают: конструкторов/операторов присваивания/деструкторов невариантных нестатических членов данных, прямых базовых классов и, где это уместно, виртуальных базовых классов (включая выражения аргументов по умолчанию, как всегда).

Каждое выражение e имеет набор потенциальных исключений . Набор пуст, если e является core constant expression , в противном случае это объединение наборов потенциальных исключений всех непосредственных подвыражений e (включая default argument expressions ), объединенное с другим набором, который зависит от формы e , как описано далее:

1) Если e является выражением вызова функции, пусть g обозначает функцию, указатель на функцию или указатель на функцию-член, который вызывается, тогда
  • если объявление g использует динамическую спецификацию исключений, набор потенциальных исключений g добавляется к множеству;
  • если объявление g использует noexcept(true) , множество пусто;
(начиная с C++11)
  • иначе, множество состоит из всех типов.
2) Если e неявно вызывает функцию (это выражение-оператор и оператор перегружен, это new-expression и функция выделения памяти перегружена, или это полное выражение и вызывается деструктор временного объекта), то множество является множеством этой функции.
3) Если e является throw-expression , множество представляет собой исключение, которое было бы инициализировано его операндом, или множество всех типов для повторно выбрасываемого throw-expression (без операнда).
4) Если e является dynamic_cast на ссылку к полиморфному типу, набор состоит из std::bad_cast .
5) Если e является typeid применённым к разыменованному указателю на полиморфный тип, множество состоит из std::bad_typeid .
6) Если e является new-expression с неконстантным размером массива, и выбранная функция выделения памяти имеет непустой набор потенциальных исключений, то этот набор состоит из std::bad_array_new_length .
(начиная с C++11)
void f() throw(int); // множество исключений f() - "int"
void g();            // множество исключений g() - множество всех типов
struct A { A(); };                  // множество исключений "new A" - множество всех типов
struct B { B() noexcept; };         // множество исключений "B()" - пустое
struct D() { D() throw (double); }; // множество исключений new D - множество всех типов

Все неявно объявленные функции-члены и наследуемые конструкторы (since C++11) имеют спецификации исключений, выбранные следующим образом:

  • Если множество потенциальных исключений — это множество всех типов, неявная спецификация исключений разрешает все исключения (спецификация исключений считается присутствующей, даже если она невыразима в коде и ведёт себя так, как если бы спецификации исключений не было) (до C++11) является noexcept ( false ) (начиная с C++11) .
  • В противном случае, если множество потенциальных исключений не пусто, неявная спецификация исключений перечисляет каждый тип из этого множества.
  • В противном случае, неявная спецификация исключений является throw ( ) (до C++11) noexcept ( true ) (начиная с C++11) .
struct A
{
    A(int = (A(5), 0)) noexcept;
    A(const A&) throw();
    A(A&&) throw();
    ~A() throw(X);
};
struct B
{
    B() throw();
    B(const B&) = default; // спецификация исключения "noexcept(true)"
    B(B&&, int = (throw Y(), 0)) noexcept;
    ~B() throw(Y);
};
int n = 7;
struct D : public A, public B
{
    // Может выбросить исключение типа, который соответствует обработчику типа
    // std​::​bad_array_new_length, но не выбрасывает исключение плохого выделения памяти
    (void*) new (std::nothrow) int[n];
    // D может иметь следующие неявно объявленные члены:
    // D::D() throw(X, std::bad_array_new_length);
    // D::D(const D&) noexcept(true);
    // D::D(D&&) throw(Y);
    // D::~D() throw(X, Y);
};

Примечания

Clang считает, что правило инстанциации спецификации динамических исключений изменено в C++11 согласно CWG1330 , см. LLVM #56349 .

Ключевые слова

throw

Пример

Примечание: рекомендуется компилировать в режиме C++98 для избежания предупреждений. Несовместимо с C++17 и новее.

#include <cstdlib>
#include <exception>
#include <iostream>
class X {};
class Y {};
class Z : public X {};
class W {};
void f() throw(X, Y) 
{
    bool n = false;
    if (n)
        throw X(); // OK, would call std::terminate()
    if (n)
        throw Z(); // also OK
    throw W(); // will call std::unexpected()
}
void handler()
{
    std::cerr << "That was unexpected!\n"; // flush needed
    std::abort();
}
int main()
{
    std::set_unexpected(handler);
    f();
}

Вывод:

That was unexpected!

Отчеты о дефектах

Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.

DR Applied to Behavior as published Correct behavior
CWG 25 C++98 поведение присваивания и инициализации
между указателями на члены с различными
спецификациями исключений было не определено
применить ограничение
для указателей на функции
и ссылок
CWG 973 C++98 спецификация исключений может содержать типы функций, но
соответствующее преобразование указателей на функции не было определено
определено
CWG 1330 C++98 спецификация исключений может быть инстанцирована преждевременно инстанцируется только при необходимости
CWG 1267 C++11 типы rvalue-ссылок были разрешены в спецификациях исключений не разрешены
CWG 1351 C++98
C++11
аргумент по умолчанию (C++98) и инициализатор члена по умолчанию
(C++11) игнорировались в неявной спецификации исключений
сделано учитываемыми
CWG 1777 C++11 throw ( T... ) не была не-генерирующей
спецификацией, даже если T является пустым пакетом
является не-генерирующей
если пакет пуст
CWG 2191 C++98 набор потенциальных исключений выражения typeid
может содержать bad_typeid даже если оно не может быть сгенерировано
содержит bad_typeid
только если оно может быть сгенерировано

Смотрите также

noexcept specifier (C++11) определяет, может ли функция генерировать исключения