Что такое 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 и производительность
-
Безопасность:
-
Явное указание намерений: выбрасывает исключения или нет.
-
Компилятор проверяет соответствие и сообщает об ошибке на этапе компиляции.
-
-
Производительность:
-
Оптимизация перемещения (std::move_if_noexcept).
-
Возможность лучшей генерации кода, устранения избыточных обработчиков.
-
-
Поведение стандартной библиотеки:
-
Стандартные контейнеры используют 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++, который сочетает надежность, контроль и производительность при работе с исключениями, особенно в шаблонном и обобщённом коде.