noexcept спецификатор (начиная с C++11)
Указывает, может ли функция выбрасывать исключения.
Синтаксис
noexcept | (1) | |
noexcept(expression) | (2) | |
throw() | (3) |
(устарело в C++17) (удалено в C++20) |
noexcept(true)
true, объявляется, что функция не выбрасывает исключений. ( после noexcept всегда является частью этой формы (оно не может начинать инициализатор).| expression | - | контекстно преобразуемое константное выражение типа bool |
Пояснение
| Спецификация noexcept не является частью типа функции (так же, как и динамическая спецификация исключений) и может появляться только как часть декларатора лямбда-выражения или декларатора функции верхнего уровня при объявлении функций, переменных, нестатических членов данных типа «функция», указателя на функцию, ссылки на функцию или указателя на функцию-член, а также при объявлении параметра или возвращаемого типа в одном из этих объявлений, если он, в свою очередь, является указателем или ссылкой на функцию. Она не может появляться в объявлении typedef или псевдонима типа. void f() noexcept; // the function f() does not throw void (*fp)() noexcept(false); // fp points to a function that may throw void g(void pfa() noexcept); // g takes a pointer to function that doesn't throw // typedef int (*pf)() noexcept; // error | (до C++17) |
| Спецификация noexcept является частью типа функции и может появляться как часть любого декларатора функции. | (начиная с C++17) |
Каждая функция в C++ либо не выбрасывает исключений, либо потенциально может выбросить исключение:
- потенциально выбрасывающими исключение являются:
| (до C++17) |
- функции, объявленные со спецификатором
noexcept, у которого expression вычисляется вfalse - функции, объявленные без спецификатора
noexcept, за исключением следующих: - деструкторы, если только деструктор любой потенциально создаваемой базовой части или члена не является потенциально выбрасывающим исключение (см. ниже)
- конструкторы по умолчанию, копирующие конструкторы и перемещающие конструкторы, которые объявлены неявно или объявлены как default при первом объявлении, если только
- конструктор базового класса или члена, вызываемый неявным определением конструктора, не является потенциально выбрасывающим исключение (см. ниже)
- подвыражение такой инициализации, например выражение аргумента по умолчанию, не является потенциально выбрасывающим исключение (см. ниже)
- инициализатор члена по умолчанию (только для конструктора по умолчанию) не является потенциально выбрасывающим исключение (см. ниже)
- операторы копирующего присваивания и перемещающего присваивания, которые объявлены неявно или объявлены как default при первом объявлении, если только вызов какого-либо оператора присваивания в неявном определении не является потенциально выбрасывающим исключение (см. ниже)
| (начиная с C++20) |
- функции освобождения памяти
- все остальные функции не выбрасывают исключений (функции со спецификатором noexcept, у которого expression вычисляется в
true, а также деструкторы, специальные функции-члены, объявленные как default, и функции освобождения памяти)
В явных инстанцированиях можно использовать спецификатор noexcept, но это необязательно. Если он используется, спецификация исключений должна совпадать со спецификациями во всех остальных объявлениях. Диагностика требуется только в том случае, если спецификации исключений различаются в пределах одной единицы трансляции.
Функции, различающиеся только спецификацией исключений, нельзя перегружать (как и возвращаемый тип, спецификация исключений является частью типа функции, но не частью сигнатуры функции)(начиная с C++17).
void f() noexcept; void f(); // error: different exception specification void g() noexcept(false); void g(); // ok, both declarations for g are potentially-throwing
Указатели (включая указатели на функции-члены) на функции, не выбрасывающие исключений, можно присваивать указателям на потенциально выбрасывающие исключение функции или использовать для их инициализации(до C++17)неявно преобразуются в указатели на потенциально выбрасывающие исключение функции(начиная с C++17), но не наоборот.
void ft(); // potentially-throwing void (*fn)() noexcept = ft; // error
Если виртуальная функция не выбрасывает исключений, все объявления каждого переопределения, включая его определение, также должны указывать, что оно не выбрасывает исключений, если только переопределение не определено как удалённое:
struct B
{
virtual void f() noexcept;
virtual void g();
virtual void h() noexcept = delete;
};
struct D: B
{
void f(); // ill-formed: D::f is potentially-throwing, B::f is non-throwing
void g() noexcept; // OK
void h() = delete; // OK
};
Функциям, не выбрасывающим исключений, разрешено вызывать функции, потенциально выбрасывающие исключение. Если выбрасывается исключение и при поиске обработчика достигается внешний блок функции, не выбрасывающей исключений, вызывается функция std::terminate:
extern void f(); // potentially-throwing
void g() noexcept
{
f(); // valid, even if f throws
throw 42; // valid, effectively a call to std::terminate
}
Спецификация исключений специализации шаблона функции не инстанцируется вместе с объявлением функции; она инстанцируется только тогда, когда это необходимо (как определено ниже).
Спецификация исключений неявно объявленной специальной функции-члена также вычисляется только при необходимости (в частности, неявное объявление функции-члена производного класса не требует инстанцирования спецификации исключений функции-члена базового класса).
Когда спецификация noexcept специализации шаблона функции необходима, но ещё не была инстанцирована, зависимые имена ищутся, а все шаблоны, используемые в expression, инстанцируются так же, как и при объявлении специализации.
Спецификация noexcept функции считается необходимой в следующих контекстах:
- в выражении, в котором функция выбрана разрешением перегрузки
- функция используется по правилу ODR
- функция использовалась бы по правилу ODR, но находится в невычисляемом операнде
template<class T>
T f() noexcept(sizeof(T) < 4);
int main()
{
decltype(f<void>()) *p; // f unevaluated, but noexcept-spec is needed
// error because instantiation of the noexcept specification
// calculates sizeof(void)
}
- спецификация необходима для сравнения с объявлением другой функции (например, переопределяющей виртуальной функции или явной специализации шаблона функции)
- в определении функции
- спецификация необходима, поскольку специальной функции-члену, объявленной как default, нужно проверить её, чтобы определить собственную спецификацию исключений (это происходит только тогда, когда сама спецификация специальной функции-члена, объявленной как default, необходима).
Формальное определение потенциально выбрасывающего исключение выражения (используется для определения спецификации исключений по умолчанию деструкторов, конструкторов и операторов присваивания, как описано выше):
Выражение e является потенциально выбрасывающим исключение, если:
-
eявляется вызовом функции, указателя на функцию или указателя на функцию-член, потенциально выбрасывающей исключение, если толькоeне является основным константным выражением(до C++17) -
eнеявно вызывает функцию, потенциально выбрасывающую исключение (например, перегруженный оператор, функцию выделения памяти в выраженииnew, конструктор аргумента функции или деструктор, еслиeявляется полным выражением) -
eявляется выражениемthrow -
eявляетсяdynamic_cast, приводящим полиморфный ссылочный тип -
eявляется выражениемtypeid, применённым к разыменованному указателю на полиморфный тип -
eимеет непосредственно входящее в него подвыражение, потенциально выбрасывающее исключение
struct A
{
A(int = (A(5), 0)) noexcept;
A(const A&) noexcept;
A(A&&) noexcept;
~A();
};
struct B
{
B() throw();
B(const B&) = default; // implicit exception specification is noexcept(true)
B(B&&, int = (throw Y(), 0)) noexcept;
~B() noexcept(false);
};
int n = 7;
struct D : public A, public B
{
int * p = new int[n];
// D::D() potentially-throwing because of the new operator
// D::D(const D&) non-throwing
// D::D(D&&) potentially-throwing: the default argument for B’s constructor may throw
// D::~D() potentially-throwing
// note; if A::~A() were virtual, this program would be ill-formed because an overrider
// of a non-throwing virtual cannot be potentially-throwing
};
Примечания
Одно из применений константного expression — вместе с оператором noexcept — определение шаблонов функций, объявляющих noexcept для одних типов, но не для других.
Обратите внимание: спецификация noexcept функции не является проверкой во время компиляции; это лишь способ сообщить компилятору, должна ли функция выбрасывать исключения. Компилятор может использовать эту информацию для включения определённых оптимизаций для функций, не выбрасывающих исключений, а также для работы оператора noexcept, который может во время компиляции проверить, объявлено ли, что конкретное выражение выбрасывает исключения. Например, контейнеры, такие как std::vector, будут перемещать свои элементы, если конструктор перемещения этих элементов — noexcept, и копировать их в противном случае (если только конструктор копирования недоступен, но доступен потенциально выбрасывающий исключение конструктор перемещения; в этом случае строгая гарантия исключений не предоставляется).
Устаревшее
noexcept — улучшенная версия throw(), которая устарела в C++11. В отличие от throw() до C++17, noexcept не вызывает std::unexpected, может раскручивать или не раскручивать стек и вызывает std::terminate, что потенциально позволяет компилятору реализовать noexcept без накладных расходов во время выполнения, присущих throw(). Начиная с C++17, throw() переопределён как точный эквивалент noexcept(true).
| Макрос проверки поддержки возможностей | Значение | Стандарт | Возможность |
|---|---|---|---|
__cpp_noexcept_function_type | 201510L | (C++17) | Включение спецификаций исключений в систему типов |
Ключевые слова
noexcept, throw(начиная с C++17)(до C++20)
Пример
// whether foo is declared noexcept depends on if the expression
// T() will throw any exceptions
template<class T>
void foo() noexcept(noexcept(T())) {}
void bar() noexcept(true) {}
void baz() noexcept { throw 42; } // noexcept is the same as noexcept(true)
int main()
{
foo<int>(); // noexcept(noexcept(int())) => noexcept(true), so this is fine
bar(); // fine
baz(); // compiles, but at runtime this calls std::terminate
}
Отчёты об ошибках
Следующие отчёты об ошибках, изменяющие поведение, были ретроактивно применены к ранее опубликованным стандартам C++.
| DR | Применяется к | Опубликованное поведение | Корректное поведение |
|---|---|---|---|
| CWG 1330 | C++11 | спецификация исключений могла инстанцироваться без необходимости | она инстанцируется только при необходимости |
| CWG 1740 | C++11 | ( после noexcept могло начинать инициализатор | оно может быть только частью спецификации noexcept |
| CWG 2039 | C++11 | требовалось, чтобы константным было только выражение до преобразования | преобразование также должно быть допустимым в константном выражении |
См. также
оператор noexcept(C++11) | определяет, выбрасывает ли выражение исключения |
| Динамическая спецификация исключений(до C++17) | указывает, какие исключения выбрасывает функция (устарело в C++11) |
Выражение throw | сигнализирует об ошибке и передаёт управление обработчику ошибок |
|
(C++11) | преобразует аргумент в xvalue, если конструктор перемещения не выбрасывает исключений (шаблон функции) |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/noexcept_spec