Namespaces
Variants

noexcept specifier (since C++11)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous
Exceptions
try block
Throwing exceptions
Handling exceptions
Exception specification
noexcept specification (C++11)
dynamic specification ( until C++17* )
noexcept operator (C++11)

Указывает, может ли функция генерировать исключения.

Содержание

Синтаксис

noexcept (1)
noexcept( expression ) (2)
throw() (3) (устарело в C++17)
(удалено в C++20)
1) То же, что и noexcept(true)
2) Если expression вычисляется как true , функция объявляется как не выбрасывающая исключений. Скобка ( после noexcept всегда является частью этой формы (она никогда не может начинать инициализатор).
3) То же, что и noexcept(true) (см. динамическую спецификацию исключений для её семантики до C++17)
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 выражение сигнализирует об ошибке и передает управление обработчику ошибок
преобразует аргумент в xvalue, если конструктор перемещения не выбрасывает исключения
(шаблон функции)