Что такое спецификатор noexcept в C++?

«Что такое спецификатор noexcept в C++?» — вопрос из категории C++ Core, который задают на 30% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

noexcept — это спецификатор и оператор, введенный в C++11, который указывает, что функция не генерирует исключений. Если функция, объявленная как noexcept, все же выбросит исключение, будет вызвана функция std::terminate(), что приведет к аварийному завершению программы.

1. Синтаксис и использование:

// 1. Функция, которая не бросает исключения
void my_function() noexcept {
    // ... код без throw
}

// 2. Условный noexcept. Функция noexcept, если выражение в скобках истинно.
//    Например, перемещающий конструктор может быть noexcept, если перемещение
//    всех членов класса не бросает исключений.
template<typename T>
void swap(T& a, T& b) noexcept(noexcept(a.swap(b))) {
    a.swap(b);
}

// 3. Оператор noexcept. Возвращает true, если выражение не генерирует исключений.
bool will_throw = noexcept(throw std::runtime_error("")); // false
bool wont_throw = noexcept(1 + 2); // true

2. Ключевые причины для использования noexcept:

  • Оптимизация: Компилятор может генерировать более эффективный код, так как ему не нужно подготавливать механизм раскрутки стека для обработки исключений.

  • Гарантии безопасности исключений в STL: Многие алгоритмы и контейнеры стандартной библиотеки используют перемещающие операции (конструктор перемещения, оператор присваивания перемещением) только если они объявлены как noexcept. В противном случае, для обеспечения строгой гарантии безопасности исключений, библиотека может откатиться к более медленному копированию.

    class MyType {
        std::vector<int> data;
    public:
        // Перемещающий конструктор с noexcept — критически важен для эффективности
        MyType(MyType&& other) noexcept : data(std::move(other.data)) {}
        // ...
    };

    При std::vector::resize, если перемещающий конструктор noexcept, элементы будут перемещены. Если нет — скопированы.

  • Улучшение читаемости интерфейса: Явно указывает на намерение разработчика и контракт функции.

3. Важные правила:

  • Деструкторы по умолчанию неявно объявлены как noexcept.
  • Все функции стандартной библиотеки, которые не предназначены для генерации исключений, помечены как noexcept.
  • noexcept является частью типа функции. Указатели на функции с noexcept и без него — разные типы.
  • Не стоит бездумно помечать все функции как noexcept. Это должно быть осознанное решение, основанное на реализации функции и операций, которые она вызывает.