Как стать автором
Обновить

Комментарии 17

Это не новая возможность C++11, в С++03 можно было использовать throw().
Фундаментально разные вещи
И в чем же их фундаментальное отличие?

Единственное небольшое отличие в контексте оптимизации про которую говорит автор заключается в том, что если функция объявленная с noexcept/throw() все таки бросит исключения, то вариант с noexcept может быть быстрее поскольку он вызывает std::terminate() перед вызовом которой стек НЕ обязан развернуться, а не std::unexpected() перед вызовом которой стек обязан развернуться.

Есть отличие заключающиеся в том что noexcept может принимать аргумент типа bool, которое может быть очень полезна совместно с ОПЕРАТОРОМ noexcept, но это отличие не связано напрямую с оптимизацией про которую писал автор.
Сами же ответили на свой вопрос. Эти два отличия и есть фундаментальная разница.
Это не фундаментальные отличия, noexcept можно рассматривать как несколько усовершенствованную версию throw() но не как что-то кардинально новое как об этом рассказывает автор. Если глянуть на стандартную библиотеку C++11 то там, где используется noexcept раньше использовался throw().
Ага, с таким же успехом операторы new/delete можно рассматривать как «несколько усовершенствованную » версию malloc/free, ведь они «всего-лишь» вызывают конструктор/деструктор в дополнение к (де)аллокации памяти.

необязятальность раскрутки стэка в случае с noexcept — это фундаментальная разница. Т.к. в деструкторах может быть могого интересных вещей типа удаления временных файлов, протокольное закрытие соединений итд итп. Поэтому прострая замена throw() на noexcept скорее всего не сработает во многих проектах.
1. По моему ваше сравнение не очень корректно: new выполняет дополнительную задачу: конструирование объекта, которая не менее важна чем выделение памяти. noexcept не выполняет дополнительной работы в сравнении с throw(), у него та же самая задача.

2. Необязательность раскрутки стэка в случае с noexcept это всего лишь оптимизация, в любом случае продолжать работу после нарушения спецификатора исключений бессмысленно поскольку это аварийная ситуация, и разница в поведении не имеет особенного значения, поэтому для noexcept оно не и определено.
Еще стоит добавить, что кроме спецификатора noexcept есть еще и оператор noexcept, который на этапе компиляции может вычислить, можно применить спецификатор noexcept или нет. Это очень полезно для шаблонов

template <class A> 
void swap (A& a, A& b) noexcept (noexcept(A::operator=))
{
    A tmp = a;
    a = b;
    b = tmp;
}


Здесь спецификатор noexcept будет включен только если A::operator= и все что он вызывает, тоже имеют спецификатор noexcept

«Благодяря» этому оператору возникла некоторая путаница в рядах с++ программистов — многие решили что спецификатор noexcept тоже выполняет проверку на этапе коипиляции. Однако, как вы правильно указали в конце статьи, это не так.
В boost постарались решить путаницу, введя макросы BOOST_NOEXCEPT, BOOST_NOEXCEPT_IF(предикат)
BOOST_NOEXCEPT_EXPR(выражение).

Если их использовать, приведенный выше пример быдет выглядеть так:

template <class A> 
void swap (A& a, A& b) BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(A::operator=))
{
    A tmp = a;
    a = b;
    b = tmp;
}

Выглядит чуточку понятнее (и немного более громоздко).

Если решите использовать оператор noexcept, стоит отметить, что компилятор GCC 4.7 достаточно часто падает с Internal Compiler Error при использовании сложных выражений.
Многовато кода генерируется компилятором, не правда ли? Именно из-за такого разбухания кода, в некоторых крупных корпорациях (не будем тыкать пальцем в Google) при разработке на С++ запрещено использование исключений. Еще одним примером могут послужить правила разработки для GCC начиная с версии 4.8 (да, GCC теперь разрабатывается с использованием С++, см изменения для 4.8).

В этом Google C++ Style Guide пункт про размер бинарника идёт четвёртым из пяти аргументов против, а в GCC Coding Conventions вовсе отсутствует.
Очевидно, разбухание кода не является столь важной проблемой для корпораций, как это подано в статье. Кроме того, эксепшены и весь сопутствующий механизм ориентированы на минимизацию оверхеда в случае, когда они не бросаются.
В этом Google C++ Style Guide пункт про размер бинарника идёт четвёртым из пяти аргументов против
noexcept так же служит и документацией к функциям (пункт второй из Google C++ Style Guide), так что уже соотношение пунктов «за» и «против» не 5:5 а где-то 5:3

эксепшены и весь сопутствующий механизм ориентированы на минимизацию оверхеда в случае, когда они не бросаются
— 100% верно для C++03, отчасти верно для C++11 в котором есть rvalue references (деструктивное копирование) и в котором уже алгоритмы подстраиваются в зависимости от спецификаторов исключений. См. «Ускорение работы стандартных алгоритмов»
Были времена, когда я не боялся кода на Си++. Но с приходом в мир нового Стандарта, что-то как-то мне всё больше не по себе. Неужели инженеры не понимают, что сложность использования языка растёт экспоненциально от количества базовых сущностей в нём? Хм… А, главное, зачем? Неужели компилятор сам не может понять, что в функции не будут выбрасываться исключения? Критерии же, если не очевидны, то ясны.
Нет, не может, если функция библиотечная, то компилятор вообще не особо может быть в курсе что у неё в теле содержится. Вот если бы в С++ были нормальные модули, то тогда бы компилятор сам мог генерировать всю дополнительную метаинформацию о функциях. И то noexcect пришлось бы вводить для точной настройки.
Если функция библиотечная (динамическая линковка), то и программист не может написать для неё noexcept (ибо, кто его знает, откуда оно там выстрелит). Если же она попадает под возможности link-time оптимизации, то информации достаточно, чтобы вывести отсутствие исключений.
Может написать разработчик библиотеки в заголовочном файле.
Эх… Может, конечно. Но зачем? Это же будет, как с const. Добавляем в интерфейс const, и всё, везде придётся писать этот const. Компилятор должен сам такие вещи выводить, ну что, у нас Haskell-а, что ли нет и не развита теория типов? Зачем C++ вести по пути COBOL-а?
Вы пишите: «Так же приготовьтесь к тому, что ваши классы исключений c перегруженным методом what() и наследуемые от стандартных, могут не компилироваться если они не помечены noexcept».
Я бы с вами поспорил, если бы вы написали «не должны компилироваться», но вы пишите «могут не компилироваться», поэтому спорить с вами я не буду. Вместо этого уточню некоторые моменты.
1. Динамические спецификации исключений (формы throw) являются запрещенными в С++11. Это означает, что теперь компиляторы могут выдавать предупреждения (но не ошибки) при их использовании. Однако я пока не работал с компиляторами, которые бы это делали (gcc 4.7.2, clang 3.2 молчат даже в режимах максимальной педантичности).
2. Спецификация throw() является совместимой с noexcept и noexcept(true) (пункт 15.4/3 стандарта). А это означает, что приведенный вами пример должен компилироваться. Более того, в вашем примере ограничений еще меньше, поскольку речь идет о переопределении функции (смотрите 15.4/5 стандарта). В принципе эта совместимость также означает, что оператор noexcept должен работать корректно и функциями со спецификацией throw() (пункт 5.3.7/3 стандарта).
На что действительно компиляторы могут выдавать предупреждение в вашем примере, так это на отсутствие квалификатора const у функции what().
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории