Можно ли запретить функции выбрасывать исключение в C++?

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

Ответ

Да, в C++ можно явно указать, что функция не должна выбрасывать исключения, с помощью спецификатора noexcept.

Синтаксис и семантика:

  • noexcept — гарантия, что функция не генерирует исключений.
  • noexcept(expression) — условная гарантия (появилась в C++11, улучшена в C++17).

Примеры:

// 1. Безусловная гарантия отсутствия исключений
void processBuffer(char* data, size_t size) noexcept {
    // Критический код, например, работа с памятью или системные вызовы.
    // Выброс исключения здесь вызовет std::terminate().
    std::memcpy(buffer, data, size);
}

// 2. Условная гарантия
template <typename T>
void swap(T& a, T& b) noexcept(std::is_nothrow_move_constructible_v<T> &&
                               std::is_nothrow_move_assignable_v<T>) {
    // Функция будет noexcept, если операции перемещения T не бросают исключений.
    T tmp = std::move(a);
    a = std::move(b);
    b = std::move(tmp);
}

// 3. Нарушение контракта (приведет к аварийному завершению)
void badFunction() noexcept {
    throw std::runtime_error("This will call std::terminate()!");
}

Практическое применение и почему это важно:

  1. Оптимизация: Компилятор может генерировать более эффективный код, зная, что внутри noexcept-функции не будет раскрутки стека из-за исключений. Это особенно важно для конструкторов перемещения, операторов присваивания перемещением и деструкторов.
  2. Гарантии безопасности: Используется в стандартной библиотеке. Например, std::vector будет использовать перемещающие операции вместо копирующих при реаллокации, только если они отмечены как noexcept, обеспечивая строгую гарантию безопасности исключений.
  3. Интерфейсный контракт: Явно сообщает пользователям кода о поведении функции, что помогает писать более надежные программы.

Важно: noexcept — это контракт, проверяемый во время выполнения. Компилятор не проверяет, действительно ли функция может выбросить исключение. Нарушение контракта приводит к вызову std::terminate().