Spec-Zone.ru › C++

noexcept спецификатор (начиная с 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; // 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 при первом объявлении, если только вызов какого-либо оператора присваивания в неявном определении не является потенциально выбрасывающим исключение (см. ниже)
  • операторы сравнения, объявленные как 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 сигнализирует об ошибке и передаёт управление обработчику ошибок
move_if_noexcept
(C++11)
преобразует аргумент в xvalue, если конструктор перемещения не выбрасывает исключений
(шаблон функции)

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/noexcept_spec

Spec-Zone.ru

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