noexcept
specifier
(since C++11)
Указывает, может ли функция генерировать исключения.
Содержание |
Синтаксис
noexcept
|
(1) | ||||||||
noexcept(
expression
)
|
(2) | ||||||||
throw()
|
(3) |
(устарело в C++17)
(удалено в C++20) |
|||||||
noexcept(true)
(
после
noexcept
всегда является частью этой формы (она никогда не может начинать инициализатор).
| expression | - | контекстно преобразованное константное выражение типа bool |
Объяснение
|
Спецификация noexcept не является частью типа функции (так же как и динамическая спецификация исключений ) и может появляться только как часть лямбда-декларатора или объявления функции верхнего уровня при объявлении функций, переменных, нестатических членов данных типа функции, указателя на функцию, ссылки на функцию или указателя на функцию-член, а также при объявлении параметра или возвращаемого типа в одном из таких объявлений, который в свою очередь оказывается указателем или ссылкой на функцию. Она не может появляться в объявлении typedef или псевдонима типа . void f() noexcept; // функция f() не выбрасывает исключений void (*fp)() noexcept(false); // fp указывает на функцию, которая может выбрасывать исключения void g(void pfa() noexcept); // g принимает указатель на функцию, которая не выбрасывает исключения // typedef int (*pf)() noexcept; // ошибка |
(до C++17) |
|
Спецификация noexcept является частью типа функции и может появляться как часть любого объявления функции . |
(начиная с C++17) |
Каждая функция в C++ является либо non-throwing , либо potentially throwing :
- potentially-throwing функции:
|
(до C++17) |
-
-
функции, объявленные с
noexceptспецификатором, чье выражение вычисляется вfalse -
функции, объявленные без
noexceptспецификатора, за исключением
-
- деструкторов , кроме случаев когда деструктор любой потенциально конструируемой базы или члена является потенциально выбрасывающим (см. ниже)
- конструкторов по умолчанию , копирующих конструкторов , перемещающих конструкторов , которые неявно объявлены или определены по умолчанию при первом объявлении, кроме случаев когда
-
- конструктор базы или члена, который вызвала бы неявная реализация конструктора, является потенциально выбрасывающим (см. ниже)
- подвыражение такой инициализации, такое как выражение аргумента по умолчанию, является потенциально выбрасывающим (см. ниже)
- инициализатор члена по умолчанию (только для конструктора по умолчанию) является потенциально выбрасывающим (см. ниже)
- операторы копирующего присваивания , операторы перемещающего присваивания , которые неявно объявлены или определены по умолчанию при первом объявлении, кроме случаев когда вызов любого оператора присваивания в неявной реализации является потенциально выбрасывающим (см. ниже)
-
функции, объявленные с
|
(начиная с C++20) |
-
функции, не генерирующие исключения, — это все остальные (те, у которых спецификатор noexcept, чьё
expression
вычисляется как
true, а также деструкторы, умолчания специальных функций-членов и функции освобождения памяти)
Явные инстанциации могут использовать спецификатор noexcept, но это не обязательно. Если он используется, спецификация исключений должна быть такой же, как и во всех других объявлениях. Диагностика требуется только в том случае, если спецификации исключений не совпадают в пределах одной единицы трансляции.
Функции, отличающиеся только спецификацией исключений, не могут быть перегружены (так же, как и возвращаемый тип, спецификация исключений является частью типа функции, но не частью сигнатуры функции) (начиная с C++17) .
void f() noexcept; void f(); // ошибка: различная спецификация исключений void g() noexcept(false); void g(); // ok, обе декларации для g являются потенциально выбрасывающими исключения
Указатели (включая указатели на функции-члены) на не выбрасывающие функции могут быть присвоены или использованы для инициализации (до C++17) являются неявно преобразуемыми в (начиная с C++17) указатели на потенциально выбрасывающие функции, но не наоборот.
void ft(); // потенциально выбрасывающая исключения void (*fn)() noexcept = ft; // ошибка
Если виртуальная функция не выбрасывает исключений, все объявления, включая определение, каждого переопределения также должны не выбрасывать исключений, за исключением случая, когда переопределение объявлено как удаленное:
struct B { virtual void f() noexcept; virtual void g(); virtual void h() noexcept = delete; }; struct D: B { void f(); // некорректно: D::f потенциально может выбрасывать исключения, B::f - нет void g() noexcept; // OK void h() = delete; // OK };
Функции, не выбрасывающие исключения, могут вызывать потенциально выбрасывающие функции. Всякий раз, когда исключение выбрасывается и поиск обработчика достигает внешнего блока функции, не выбрасывающей исключения, вызывается функция std::terminate :
extern void f(); // потенциально выбрасывающая исключения void g() noexcept { f(); // корректно, даже если f выбрасывает исключение throw 42; // корректно, фактически вызывает std::terminate }
Спецификация исключений специализации шаблона функции не инстанцируется вместе с объявлением функции; она инстанцируется только тогда, когда требуется (как определено ниже).
Спецификация исключений неявно объявленной специальной функции-члена также вычисляется только когда это необходимо (в частности, неявное объявление функции-члена производного класса не требует инстанцирования спецификации исключений базовой функции-члена).
Когда спецификация noexcept специализации шаблона функции требуется , но еще не была инстанцирована, зависимые имена подвергаются поиску, и любые шаблоны, используемые в expression , инстанцируются, как если бы для объявления специализации.
Спецификация noexcept функции считается необходимой в следующих контекстах:
- в выражении, где функция выбирается через разрешение перегрузки
- функция является odr-used
- функция была бы odr-used, но появляется в невычисляемом операнде
template<class T> T f() noexcept(sizeof(T) < 4); int main() { decltype(f<void>()) *p; // f не вычисляется, но требуется спецификация noexcept // ошибка, потому что инстанцирование спецификации noexcept // вычисляет sizeof(void) }
- спецификация необходима для сравнения с другим объявлением функции (например, при переопределении виртуальной функции или явной специализации шаблона функции)
- в определении функции
- спецификация необходима, поскольку специальная функция-член по умолчанию должна проверить её, чтобы определить свою собственную спецификацию исключений (это происходит только тогда, когда спецификация специальной функции-члена по умолчанию сама по себе необходима).
Формальное определение потенциально выбрасывающего выражения (используется для определения спецификации исключений по умолчанию для деструкторов, конструкторов и операторов присваивания, как описано выше):
Выражение
e
является
потенциально-генерирующим исключения
если:
-
eпредставляет вызов функции, указателя на функцию или указателя на функцию-член, которые являются потенциально-генерирующими исключения , если толькоeне является core constant expression (до 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; // неявная спецификация исключений - 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() потенциально выбрасывающий исключения из-за оператора new // D::D(const D&) не выбрасывающий исключения // D::D(D&&) потенциально выбрасывающий: аргумент по умолчанию для конструктора B может выбрасывать исключения // D::~D() потенциально выбрасывающий исключения // примечание: если бы A::~A() был виртуальным, программа была бы некорректной, потому что переопределитель // не выбрасывающего виртуального метода не может быть потенциально выбрасывающим };
Примечания
Одно из применений константных
выражений
(наряду с
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 | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| CWG 1330 | C++11 | an exception specification might be eagerly instantiated | it is only instantiated only if needed |
| CWG 1740 | C++11 | a ( following noexcept might start an initializer |
it can only be a part of
noexcept specification |
| CWG 2039 | C++11 | only the expression before conversion is required to be constant |
the conversion must also be
valid in a constant expression |
Смотрите также
noexcept
оператор
(C++11)
|
определяет, выбрасывает ли выражение какие-либо исключения |
| Динамическая спецификация исключений (до C++17) | определяет, какие исключения выбрасываются функцией (устарело в C++11) |
throw
выражение
|
сигнализирует об ошибке и передает управление обработчику ошибок |
|
(C++11)
|
преобразует аргумент в xvalue, если конструктор перемещения не выбрасывает исключения
(шаблон функции) |