Разница между спецификатором throw () C ++ 03 C ++ 11 noexcept

100

Есть ли какая-то разница между throw()и, noexceptкроме проверки во время выполнения и во время компиляции, соответственно?

В этой статье Wikipedia C ++ 11 предполагается, что спецификаторы throw C ++ 03 устарели.
Почему так, noexceptдостаточно ли для того, чтобы охватить все это во время компиляции?

[Примечание: я проверил этот вопрос и эту статью , но не смог определить вескую причину прекращения поддержки.]

iammilind
источник
7
Согласно этой хорошей статье, также noexceptможет потребоваться проверка во время выполнения. Основное различие между ними в том , что ломая noexceptпричины std::terminate, разбивая throwпричины std::unexpected. Также в этих случаях немного другое поведение при раскручивании стека.
Fiktik
Ничего не проверяется во время компиляции, за исключением некоторых спецификаций исключений, в других проверяется время выполнения. Это просто миф, созданный противниками спецификаций исключений C ++.
curiousguy

Ответы:

129

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

noexceptбыл добавлен вместо того, чтобы просто удалить все спецификаторы выброса, кроме того, throw()что noexceptон более мощный. noexceptможет иметь параметр, который во время компиляции преобразуется в логическое значение. Если логическое значение истинно, то noexceptприлипает. Если логическое значение false, то noexceptфункция не прилипает и функция может выбросить.

Таким образом, вы можете сделать что-то вроде этого:

struct<typename T>
{
  void CreateOtherClass() { T t{}; }
};

Выбрасывает CreateOtherClassисключения? Может, если Tконструктор по умолчанию может. Как мы узнаем? Как это:

struct<typename T>
{
  void CreateOtherClass() noexcept(is_nothrow_default_constructible<T>::value) { T t{}; }
};

Таким образом, CreateOtherClass()будет сгенерировано, если конструктор по умолчанию данного типа выбрасывает. Это устраняет одну из основных проблем со спецификаторами исключений: их неспособность распространяться вверх по стеку вызовов.

Вы не можете этого сделать с throw().

Никол Болас
источник
+1 Полезный ответ, по крайней мере для меня. Все еще ищу ответ, в котором говорится, почему я хочу использовать noexcept. Я никогда не использовал throw()спецификатор и пытаюсь определить, noexceptдействительно ли он дает какие-либо преимущества (кроме проверенной компилятором документации).
hmjd
Только что нашел этот stackoverflow.com/questions/10787766/… ...
hmjd
1
@NicolBolas согласен. но если noexcept будет гарантией, компилятор может проверить, может ли функция вызывать деструктор. Таким образом можно предупредить программиста о том, что функция не является исключением или нет.
Alex
2
@NicolBolas вызовы среды выполнения std::terminate. что НАМНОГО ХУЖЕ ! код может проникнуть в выпуски, в которых отмечены функции, noexcept и во время выполнения (то есть на сайтах клиентов) обнаруживаются нарушения. Я имел в виду гарантии компилятор для генерации кода , который не бросать исключения в первую очередь.
Alex
2
@NicolBolas: Следует отметить еще одно отличие. Если функция отмечена, throws()то при возникновении исключения стек должен быть размотан до области действия этой функции (так, чтобы все автоматические переменные в функции были уничтожены), после чего terminate()вызывается (через unexpected()). Если функция отмечена, noexceptто при возникновении исключения вызывается terminate (разворачивание стека определяется реализацией).
Мартин Йорк
33

noexcept не проверяется во время компиляции.

Реализация не должна отклонять выражение только потому, что при выполнении оно вызывает или может вызвать исключение, которое не позволяет функция-контейнер.

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

CB Bailey
источник
Но если у виртуальной функции есть throw()/ noexcept, проверка времени компиляции гарантирует, что переопределитель также имеет.
curiousguy
2

std::unexpected() вызывается средой выполнения C ++, когда нарушается спецификация динамического исключения: исключение выбрасывается из функции, спецификация исключения которой запрещает исключения этого типа.

std::unexpected() также может быть вызван прямо из программы.

В любом случае std::unexpectedвызывает установленный в данный момент std::unexpected_handler. std::unexpected_handlerЗвонки по умолчанию std::terminate.

ma13
источник