Spec-Zone.ru › C++

спецификатор noexcept (с C++11)

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

Синтаксис

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

Ключевые слова

noexcept

Пример

// 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)
получает ссылку на rvalue, если конструктор перемещения не выбрасывает исключения
(шаблон функции)

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

Spec-Zone.ru

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