Динамическая спецификация исключений (до C++17)
Перечисляет исключения, которые функция может выбрасывать напрямую или косвенно.
Синтаксис
throw(список-типов-идентификаторов (необязательно)) | (1) | (устаревшее в C++11) (удалено в C++17) |
| список-типов-идентификаторов | - | список типов-идентификаторов, разделённых запятыми., тип-идентификатор, представляющий расширение пакета, следует за многоточием (...)(с C++11) |
Явное динамическое указание на исключения должно появляться только в деклараторе функции для типа функции, указателя на тип функции, ссылки на тип функции или указателя на член-функцию типа, являющегося основным типом объявления или определения, или в таком типе, появляющемся в качестве параметра или возвращаемого типа в деклараторе функции.
void f() throw(int); // OK: function declaration void (*pf)() throw (int); // OK: pointer to function declaration void g(void pfa() throw(int)); // OK: pointer to function parameter declaration typedef int (*pf)() throw(int); // Error: typedef declaration
Объяснение
Если функция объявлена с типом T в списке динамического указания на исключения, функция может выбрасывать исключения этого типа или типа, производного от него.
Неполные типы, указатели или ссылки на неполные типы, кроме cv void*, и типов ссылок на правые значения(с 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.
Инициализация
Динамическая спецификация исключений специализации шаблона функций не инициализируется вместе с объявлением функции; она инициализируется только при необходимости (как определено ниже).
Динамическая спецификация исключений неявно объявленной специальной функции-члена также вычисляется только при необходимости (в частности, неявное объявление функции-члена производного класса не требует инициализации спецификации исключений базовой функции-члена).
Когда динамическая спецификация исключений специализации шаблона функции необходима, но ещё не инициализирована, зависимые имена ищутся, а любые шаблоны, используемые в выражении, инициализируются как если бы для объявления специализации.
Динамическая спецификация исключений функции считается необходимой в следующих контекстах:
- в выражении, где функция выбирается с помощью разрешения перегрузки
- функция используется odr-используется
- функция была бы odr-использована, но появляется в неоцениваемом операнде
template<class T>
T f() throw(std::array<char, sizeof(T)>);
int main()
{
decltype(f<void>()) *p; // f unevaluated, but exception specification is needed
// error because instantiation of the exception specification
// calculates sizeof(void)
}- спецификация необходима для сравнения с другим объявлением функции (например, при переопределении виртуальной функции или при явной специализации шаблона функции)
- в определении функции
- спецификация необходима, потому что неявно определённой специальной функции-члену необходимо проверить её, чтобы определить собственную спецификацию исключений (это происходит только тогда, когда спецификация неявно определённой специальной функции-члена сама по себе необходима).
Возможные исключения
Каждая функция f, указатель на функцию pf, и указатель на функцию-член pmf имеет множество потенциальных исключений, которое состоит из типов, которые могут быть сгенерированы. Множество всех типов означает, что может быть сгенерировано любое исключение. Это множество определяется следующим образом:
f, pf, или pmf использует динамическую спецификацию исключений которая не допускает всех исключений(до C++11), множество состоит из типов, указанных в этой спецификации. 2) В противном случае, если объявление f, pf, или pmf использует noexcept(true), множество пусто. | (с C++11) |
Примечание: для неявно объявленных специальных функций-членов (конструкторов, операторов присваивания и деструкторов) и для конструкторов наследования(с C++11), множество потенциальных исключений представляет собой комбинацию множеств потенциальных исключений всего, что они вызывают: конструкторы/операторы присваивания/деструкторы невариантных нестатических данных, прямых базовых классов и, при необходимости, виртуальных базовых классов (включая выражения аргументов по умолчанию, как всегда).
У каждого выражения e есть множество потенциальных исключений. Множество пусто, если e является ядерным константным выражением, в противном случае оно представляет собой объединение множеств потенциальных исключений всех непосредственных подвыражений e (включая выражения аргументов по умолчанию), в сочетании с другим множеством, которое зависит от формы e, как показано ниже:
e является выражением вызова функции, пусть g обозначает функцию, указатель на функцию или указатель на функцию-член, которая вызывается, тогда - если объявление
gиспользует динамическую спецификацию исключений, множество потенциальных исключенийgдобавляется в множество;
| (с C++11) |
- в противном случае, множество содержит все типы.
e неявно вызывает функцию (это выражение оператора, и оператор перегружен, это выражение new, и функция выделения перегружена, или это полное выражение, и деструктор временного объекта вызывается), то множество представляет собой множество этой функции.e является выражением throw, множество представляет собой исключение, которое должно быть инициализировано его операндом, или множество всех типов для выражения throw, перебрасывающего исключение (без операнда).e является typeid, применённым к дереференцированному указателю на полиморфный тип, множество состоит из std::bad_typeid. 6) Если e является выражением new с неконстантным размером массива, и выбранная функция выделения имеет непустое множество потенциальных исключений, множество состоит из std::bad_array_new_length. | (с C++11) |
void f() throw(int); // f()'s set is "int"
void g(); // g()'s set is the set of all types
struct A { A(); }; // "new A"'s set is the set of all types
struct B { B() noexcept; }; // "B()"'s set is empty
struct D() { D() throw (double); }; // new D's set is the set of all typesВсе неявно объявленные функции-члены и конструкторы наследования (с 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; // exception specification is "noexcept(true)"
B(B&&, int = (throw Y(), 0)) noexcept;
~B() throw(Y);
};
int n = 7;
struct D : public A, public B
{
// May throw an exception of a type that would match a handler of type
// std::bad_array_new_length, but does not throw a bad allocation exception
(void*) new (std::nothrow) int[n];
// D may have the following implicitly-declared members:
// 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.
Пример
Примечание: лучше всего компилировать в режиме 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 | Применено к | Поведение, как опубликовано | Правильное поведение |
|---|---|---|---|
| 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(C++11) | указывает, может ли функция выбрасывать исключения |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/except_spec