Подтвердить что ты не робот

Почему noexcept не применяется во время компиляции?

Как вы знаете, у С++ 11 есть ключевое слово noexcept. Теперь уродливая часть об этом:

Обратите внимание, что спецификация noexcept для функции не является временем компиляции проверить; это всего лишь способ программиста сообщить компилятору независимо от того, должна ли функция генерировать исключения.

http://en.cppreference.com/w/cpp/language/noexcept_spec

Так что это сбой дизайна в части комитета или они просто оставили его как упражнение для компиляторов:) в некотором смысле, что достойные компиляторы будут его применять, а плохие могут быть совместимы?

Кстати, если вы спросите, почему нет третьего варианта (иначе это может быть сделано), причина в том, что я могу легко думать о (медленном) способе проверить, может ли функция выбраться или нет. Проблема не в курсе, если вы ограничиваете ввод 5 и 7 (иначе я обещаю, что файл не будет содержать ничего рядом с 5 и 7), и он бросает только тогда, когда вы даете ему 33, но это нереальная проблема ИМХО.

4b9b3361

Ответ 1

Комитет довольно четко рассмотрел возможность того, что код, который (попытался) исключить исключение, не допущенное спецификацией исключения, будет считаться плохо сформированным и отвергнет эту идею. Согласно $15.4/11:

Реализация не должна отвергать выражение только потому, что при его выполнении он генерирует или может вызывать исключение, которое не допускает содержащая функция. [Пример:

  extern void f() throw(X, Y);

  void g() throw(X) {
      f(); // OK
  }

вызов f корректно формируется, хотя при вызове f может генерироваться исключение Y, которое g не разрешает. -end пример]

Независимо от того, что подтолкнуло решение или что еще могло быть, кажется довольно очевидным, что это не было результатом аварии или недосмотра.

Что касается того, почему это решение было принято, по крайней мере некоторые возвращаются к взаимодействию с другими новыми функциями С++ 11, такими как семантика перемещения.

Перемещение семантики может сделать безопасность исключений (особенно сильную гарантию) намного сложнее обеспечить/обеспечить. Когда вы копируете, если что-то пойдет не так, довольно легко "откат" транзакции - уничтожить все сделанные вами копии, освободить память, а оригинал останется нетронутым. Только если/когда копия завершается успешно, вы уничтожаете оригинал.

С семантикой перемещения это сложнее - если вы получаете исключение в середине движущихся вещей, все, что вы уже переместили, должно быть возвращено туда, где нужно было восстановить оригинал на заказ, - но если перемещать конструктор или оператор присваивания перемещения можно бросить, вы можете получить еще одно исключение в процессе попытки переместить все, чтобы попытаться восстановить исходный объект.

Объедините это с тем фактом, что С++ 11 может/генерировать конструкторы перемещения и автоматически переносить операторы присваивания для некоторых типов (хотя существует длинный список ограничений). Они не обязательно гарантируют отбрасывание исключения. Если вы явно пишете конструктор перемещения, вы почти всегда хотите, чтобы его не бросали, и что обычно даже довольно легко сделать (поскольку вы обычно "крадете" контент, вы обычно просто копируете несколько указателей - легко обойтись без исключений). Это может стать намного сложнее в шаблоне, хотя даже для простых, таких как std:pair. Пару чего-то, что можно переместить с чем-то, что нужно скопировать, становится трудно справиться.

Это означало, что если бы они решили сделать nothrow (и/или throw()) принудительно во время компиляции, неизвестный (но, вероятно, довольно большой) код был бы полностью сломан - код, который отлично работали годами, даже не компилировались с новым компилятором.

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

Итак, их выбор:

  • Перерыв в существующем коде
  • Ограничить семантику перемещения, чтобы они применялись к гораздо меньшему количеству кода
  • Продолжить (как в С++ 03) для обеспечения спецификаций исключений во время выполнения.

Я сомневаюсь, что кто-то понравился любой из этих выборов, но третий, по-видимому, казался последним плохим.

Ответ 2

Одна из причин заключается в том, что принудительное исполнение спецификаций исключений (любого вкуса) во время компиляции - это боль в заднице. Это означает, что если вы добавите код отладки, вам может потребоваться переписать всю иерархию спецификаций исключений, даже если добавленный код не будет генерировать исключения. И когда вы закончите отладку, вам придется переписать их еще раз. Если вам нравится этот вид работы, вы должны программировать на Java.

Ответ 3

Проблема с проверкой времени компиляции: это действительно не возможно каким-либо полезным способом. См. Следующий пример:

void foo(std::vector<int>& v) noexcept
{
    if (!v.empty())
        ++v.at(0);
}

Может ли этот код выбросить? Совершенно очевидно. Мы можем проверить автоматически? На самом деле, нет. Java-способ делать такие вещи - это поместить тело в блок try-catch, но я не думаю, что это лучше, чем у нас сейчас...

Ответ 4

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

Функции вызова, которые не указывают, что они бросают или не бросают, должны рассматриваться как потенциально бросить что-нибудь вообще! Таким образом, компилятор должен был требовать, чтобы вы не бросали и не вызывали ничего, что могло бы выкинуть что-либо за пределами спецификации, которую вы предоставили, на самом деле навязывали такую ​​вещь, ваш код мог бы почти ничего не вызывать, никакая библиотека не имела бы никакого смысла вам или кому-либо еще, кто пытается использовать спецификации броска.

И поскольку компилятор не может сказать разницу между "Эта функция может вызывать X, но вызывающий может вызвать его таким образом, чтобы он никогда ничего не бросал" - навсегда быть hamstrung с помощью этого языка "особенность."

Итак... Я считаю, что единственная возможная вещь, которая могла бы возникнуть, заключалась в том, чтобы говорить о nothrow - что указывает на то, что безопасно звонить из dtors, перемещаться и свопыть и т.д., но вы делаете обозначение, которое, как и const, - это больше о том, чтобы предоставить вашим пользователям контракт API, а не фактически заставить компилятор ответить, нарушаете ли вы свой контракт или нет (например, с большинством вещей C/С++ - предполагается, что интеллект со стороны программиста, а не няни-компилятора).