Что такое noexcept


noexcept — это спецификатор исключений, введённый в C++11, который сообщает компилятору и программисту, гарантирует ли функция отсутствие выбрасывания исключений. Его основное назначение — улучшение безопасности, производительности и предсказуемости кода, особенно при работе с шаблонами, контейнерами и перемеща semantics.

📌 Синтаксис и формы использования

1. Базовый синтаксис

void func() noexcept; // Гарантирует, что func() не выбрасывает исключений

void other() noexcept(false); // Явно указано, что может выбрасывать исключения

2. noexcept с выражением

template<typename T>

void call(T t) noexcept(noexcept(t.doSomething())) {

t.doSomething();

}

Здесь noexcept(expr) — это выражение, которое возвращает true или false на этапе компиляции. Оно зависит от того, гарантирует ли t.doSomething() отсутствие исключений.

🧠 Отличие от throw()

До C++11 использовался спецификатор throw():

void f() throw(); // Устаревшее в C++11, удалено в C++17

throw() считалось эквивалентом noexcept(true), но throw() имело менее надёжное поведение (не всегда проверялось компилятором). noexcept — строгое и современное средство.

🚨 Поведение при нарушении noexcept

Если функция, объявленная как noexcept, всё же выбрасывает исключение:

  • Вызывается std::terminate(), и программа аварийно завершается.

  • Это может быть предпочтительнее, чем продолжение выполнения с нарушением инвариантов.

Пример:

void dangerous() noexcept {

throw std::runtime_error("Ошибка!");

}

// вызов dangerous(); → вызовет std::terminate

📦 Использование с конструкторами и операторами перемещения

std::vector, std::swap, std::move, и другие стандартные контейнеры оптимизируют поведение, если перемещающий конструктор/оператор помечены noexcept.

class A {

public:

A(A&&) noexcept; // очень важно для производительности вектора

};

Если A::A(A&&) не помечен noexcept, контейнер при перемещении элементов может предпочесть копирование, что дороже.

🔍 noexcept в шаблонах

В шаблонном коде noexcept выражения помогает адаптировать поведение функции в зависимости от типа параметра:

template<typename T>

void wrapper(T&& t) noexcept(noexcept(process(std::forward<T>(t)))) {

process(std::forward<T>(t));

}

Компилятор подставит true или false в noexcept в зависимости от исключаемости вызова process.

🧪 Пример: noexcept как выражение

void f() noexcept(true); // никогда не бросает

void g() noexcept(false); // может бросать

constexpr bool f_noexcept = noexcept(f()); // true

constexpr bool g_noexcept = noexcept(g()); // false

🛠 noexcept operator

Можно перегружать оператор noexcept как унарный оператор, возвращающий bool:

int x = 10;

bool result = noexcept(++x); // true, т.к. инкремент не бросает исключений

📊 noexcept и производительность

  1. Безопасность:

    • Явное указание намерений: выбрасывает исключения или нет.

    • Компилятор проверяет соответствие и сообщает об ошибке на этапе компиляции.

  2. Производительность:

    • Оптимизация перемещения (std::move_if_noexcept).

    • Возможность лучшей генерации кода, устранения избыточных обработчиков.

  3. Поведение стандартной библиотеки:

    • Стандартные контейнеры используют noexcept-конструкторы при реаллокации.

    • Алгоритмы используют std::is_nothrow_move_constructible для оптимизаций.

📋 Проверка noexcept-совместимости

Можно использовать std::is_nothrow_invocable, std::is_nothrow_move_constructible и др.:

include <type_traits>

static_assert(std::is_nothrow_move_constructible<std::string>::value, "string must be noexcept move constructible");

🧱 noexcept в функциях-членах

struct X {

void f() const noexcept;

void g() noexcept;

void h() & noexcept; // только lvalue

void i() && noexcept; // только rvalue

};

Можно использовать noexcept вместе с const, ссылочной квалификацией и override.

📌 noexcept vs try-catch

  • noexcept не перехватывает исключения, а запрещает их распространение.

  • try-catch — обработка исключений во время выполнения.

  • noexcept — гарантия отсутствия исключений во время компиляции (или сигнал о нарушении — через terminate()).

🔄 Связь с другими концепциями

Особенность noexcept try/catch
Этап Компиляция Выполнение
--- --- ---
Цель Оптимизация, контроль noexcept Обработка исключений
--- --- ---
При нарушении условий std::terminate() Переход в catch
--- --- ---
Использование в шаблонах Да Нет
--- --- ---
Производительность Повышает Может снижать
--- --- ---

noexcept — важный инструмент современного C++, который сочетает надежность, контроль и производительность при работе с исключениями, особенно в шаблонном и обобщённом коде.