Почему старая спецификация пустого броска была переписана с новым синтаксисом noexcept? - PullRequest
0 голосов
/ 27 октября 2019

Заголовок говорит сам за себя: почему C ++ отказался от совершенно удовлетворяющей, полезной спецификации пустого броска throw(), чтобы заменить ее другим синтаксисом с введением нового ключевого слова noexcept?

Пустая спецификация броска является «гарантией только для этих перечисленных исключений» (записано throw(X,Y,Z)), но с нулевыми перечисляемыми исключениями: вместо броска X, Y или Z (и производных типов), вы можете выбросить пустой набор: , который является гарантией того, что функция никогда не бросит что-либо в вызывающую сторону, другими словами, спецификацию "никогда не выбрасывать" или "не выбрасывать" .

То, что бесплатно сделал новый код, используя по существу тот же инструмент, выражая то же обещание, несовместимо со старым кодом , а старый код устарел и затем запрещен, нарушая обратную совместимость без видимой причины?

Что вызвало такую ​​ненависть к throw()?

Только старые небезопасные gets и глупые бесполезные неявные int рассматривались как харсыhly, насколько я могу судить.

EDIT:

Предполагаемый "дубликат" основан на ложном утверждении.

В так называемой "спецификации динамических исключений" нет ничего "динамического". Это то, что я больше всего ненавижу в новой спецификации броска: значение бессмысленной терминологии, которая выступает против "динамического". "и" статический ".

1 Ответ

4 голосов
/ 27 октября 2019

Это безвозмездно сделал новый код, используя по существу тот же инструмент, выражающий то же обещание, несовместимое со старым кодом

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

Обещания разные. throw() означало «может делать неожиданные вещи, если пытается создать исключение». noexcept означает «немедленно прекратит работу, если попытается создать исключение».

Это было только в C ++ 17, когда throw() было сделано в точности эквивалентным noexcept. Это произошло после 6 лет исключений спецификаций (включая throw()).

Следует отметить, что разница unexpected была явно процитирована в первых статьях о noexcept,В частности:

Обратите внимание, что полезность noexcept(true) в качестве подсказки по оптимизации выходит далеко за рамки узкого случая, представленного N2855. Фактически, это выходит за рамки построения перемещения: когда компилятор может с уверенностью обнаруживать не-генерирующие операции, он может оптимизировать большое количество кода и / или данных, предназначенных для обработки исключений. Некоторые компиляторы уже делают это для throw() спецификаций, но, поскольку они несут накладные расходы неявного блока try / catch для обработки непредвиденных исключений, преимущества ограничены.

Неявный блок try / catchнеобходимо, потому что unexpected должен вызываться после разматывания стека к функции throw(). По сути, каждая функция throw() выглядит следующим образом:

void func(params) throw()
try
{
  <stuff>
}
catch(...)
{
  std::unexpected();
}

Поэтому, когда исключение пытается покинуть функцию throw(), исключение получает перехвачено и стек разворачивается. Более того, каждая функция throw() должна иметь встроенный механизм исключения. Таким образом, независимо от стоимости блока try/catch будет взиматься каждая throw() функция.

В первой версии noexcept исключение было прямым UB, а более поздние версии переключались на std::terminate. Но даже с этим, нет гарантии, что раскрутить. Таким образом, реализации могут реализовать noexcept более эффективным способом. Когда система ищет в стеке ближайшее предложение catch, если оно достигает дна функции noexcept, она может идти прямо к завершению без какого-либо механизма захвата.

Чтовызвал такую ​​ненависть к throw ()?

Исправление: не повторное назначение конструкции не влечет за собой злого умысла. Особенно, когда изобретение новой конструкции позволит избежать нарушения совместимости, как показано выше.

Следует отметить, что throw() считается эквивалентным функции noexcept в терминах выражения noexcept. То есть вызов функции throw() не вызывает исключений, а выражение noexcept(empty_throw()) приведет к true.

Следует также отметить, что в любом случае потребуется новое ключевое слово. Почему? Потому что throw(<stuff>) в C ++ 98/03 уже имеет значение. noexcept(<stuff>) имеет совсем другое значение для <stuff>. Попытка поместить материал noexcept внутрь спецификатора throw будет ... трудной, с точки зрения синтаксического анализа.

Кроме того, теперь вы можете использовать это новое ключевое слово в качестве обобщенного выражения: noexcept(<expression>)разрешается до true, если ни один из вызовов внутри него не выдаст исключения. Это позволяет вам делать условную логику в зависимости от того, будут ли вещи вызывать исключения. Вам понадобится новое ключевое слово, чтобы сделать что-то подобное (или вам придется создать уродливый синтаксис, а в C ++ слишком много этого происходит).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...