Spec-Zone.ru › C++

Динамическая спецификация исключений (до C++17)

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

Синтаксис

throw(список-типов-идентификаторов (необязательно)) (1) (устаревшее в C++11)
(удалено в C++17)
1) Явное указание на динамическое исключение.
список-типов-идентификаторов - список типов-идентификаторов, разделённых запятыми., тип-идентификатор, представляющий расширение пакета, следует за многоточием (...)(с 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 имеет множество потенциальных исключений, которое состоит из типов, которые могут быть сгенерированы. Множество всех типов означает, что может быть сгенерировано любое исключение. Это множество определяется следующим образом:

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

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

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

1) Если e является выражением вызова функции, пусть g обозначает функцию, указатель на функцию или указатель на функцию-член, которая вызывается, тогда
  • если объявление g использует динамическую спецификацию исключений, множество потенциальных исключений g добавляется в множество;
  • если объявление g использует noexcept(true), множество пусто;
(с C++11)
  • в противном случае, множество содержит все типы.
2) Если e неявно вызывает функцию (это выражение оператора, и оператор перегружен, это выражение new, и функция выделения перегружена, или это полное выражение, и деструктор временного объекта вызывается), то множество представляет собой множество этой функции.
3) Если e является выражением throw, множество представляет собой исключение, которое должно быть инициализировано его операндом, или множество всех типов для выражения throw, перебрасывающего исключение (без операнда).
4) Если e является dynamic_cast к ссылке на полиморфный тип, множество состоит из std::bad_cast.
5) Если 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API