Spec-Zone.ru › C++

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

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

Синтаксис

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

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

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

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

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

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

  • в выражении, в котором функция выбрана посредством разрешения перегрузки
  • функция является 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, набор состоит из исключения, которое было бы инициализировано его операндом, либо из всех типов для повторного выбрасывания исключения без операнда.
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.

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

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 Применён к Опубликованное поведение Корректное поведение
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/cpp/language/except_spec

Spec-Zone.ru

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