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


100

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

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

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


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а последний стиль обработки исключений фактически устарел.


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

2

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.