спецификатор noexcept (с C++11)
Указывает, может ли функция выбрасывать исключения.
Синтаксис
noexcept | (1) | |
noexcept(выражение) | (2) | |
throw() | (3) | (устарело в C++17) (удалено в C++20) |
noexcept(true)
true, функция объявлена как не выбрасывающая исключений. ( после noexcept всегда является частью этого формата (он никогда не может начинать инициализатор).| выражение | - | контекстно преобразуемое константное выражение типа 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, чье выражение вычисляется какfalse - функции, объявленные без спецификатора
noexceptза исключением - деструкторов, если деструктор любого потенциально конструируемого базового класса или члена является потенциально выбрасывающим (см. ниже)
- конструкторов по умолчанию, копирующих конструкторов, перемещающих конструкторов, которые неявно объявлены или заданы по умолчанию при первом объявлении, если
- конструктор базового класса или члена, который неявно объявленный конструктор должен вызвать, является потенциально выбрасывающим (см. ниже)
- подвыражение такой инициализации, например, выражение аргумента по умолчанию, является потенциально выбрасывающим (см. ниже)
- инициализатор члена по умолчанию (только для конструктора по умолчанию) является потенциально выбрасывающим (см. ниже)
- операторы копирующей присваивания, перемещающей присваивания, которые неявно объявлены или заданы по умолчанию при первом объявлении, если вызов любого оператора присваивания в неявном определении является потенциально выбрасывающим (см. ниже)
| (с C++20) |
- функции выделения/освобождения памяти
- не выбрасывающие функции - все остальные (те, у которых спецификатор noexcept, чье выражение вычисляется как
true, а также деструкторы, функции-члены по умолчанию и функции выделения/освобождения памяти)
Явные инстанцирования могут использовать спецификатор 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 специализации шаблона функции необходима, но еще не инстанцирована, зависимые имена ищут и любые шаблоны, используемые в выражении, инстанцируются, как если бы для объявления специализации.
Спецификация 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)
}- спецификация необходима для сравнения с другим объявлением функции (например, при переопределении виртуальной функции или при явной специализации шаблона функции)
- в определении функции
- спецификация необходима, поскольку функция-член по умолчанию должна проверить ее для определения собственной спецификации исключения (это происходит только тогда, когда спецификация самой функции-члена по умолчанию сама по себе необходима).
Формальное определение выражения, потенциально выбрасывающего исключения (используется для определения спецификации исключений по умолчанию для деструкторов, конструкторов и операторов присваивания, как описано выше):
Выражение 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
};Примечания
Одно из применений константного выражения (наряду с оператором 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).
| Макрокоманда проверки функции | Значение | Std | Функция |
|---|---|---|---|
__cpp_noexcept_function_type | 201510L | (C++17) | Сделать спецификации исключений частью системы типов |
Ключевые слова
Пример
// 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) | получает ссылку на rvalue, если конструктор перемещения не выбрасывает исключения (шаблон функции) |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/noexcept_spec