Существовали ли какие-либо версии C ++ (даже предстандартные), в которых `throw ()` не означало "не могу бросить, никогда"? - PullRequest
1 голос
/ 28 октября 2019

Вопросы касаются истории C ++: стандарты ISO, пересмотренные стандарты (с DR), даже проекты стандартов;все считаются как "C ++".

Есть ли C ++, в котором это свойство не удерживается:

Функция, объявленная с пустой спецификацией throw throw() не может сгенерировать исключение.

Мне нужен контрпример, если это свойство не сохраняется.

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

Само собой разумеется, что бросаниезатем перехватывает (не перебрасывая) исключение внутри функции, превращает ли это информацию в «функцию, генерирующую исключение». Функция, выбрасывающая исключение, в соответствии со своей спецификацией, выдает ее вызывающей стороне. (Если вы делаете вещи внутренне, это не является частью спецификации по определению.)

[Допустим, что longjmp запрещено, поскольку у нас есть разрушаемые объекты.]

Другие эквивалентные выраженияэтот вопрос:

Был ли когда-нибудь случай, когда вызывающая функция, объявленная без броска, должна была беспокоиться о возможности (эквивалентные свойства):

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

Другими словами, был ли C ++ , где компилятор не мог оптимизировать вызывающую функцию , основываясь на просмотре объявления вызова без вызова ?

Ответы [ 2 ]

3 голосов
/ 28 октября 2019

C ++ 17 устарел throw как функция аннотации, в основном. Он по-прежнему допускает throw() и считает его эквивалентным noexcept(true). Это означает, что throw() является признаком того, что функция не должна выходить из-за исключения. Нарушение гарантии noexcept(true) приводит к неопределенному поведению.

Семантика throw() до C ++ 17 была различна и различна. Раньше обещали, что вызов ::std::unexpected() произойдет, если будет нарушено предложение throw() (т. Е. Было сгенерировано исключение, результатом которого является выход из функции).

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

Этот код в проводнике компилятора демонстрирует обработчик «непредвиденного исключения» . Если вы измените запрошенный стандарт на C ++ 17, вы увидите, что код этого исчезнет.

extern void a_different_function();

void test_function() throw ()
{
    a_different_function();
}
1 голос
/ 29 октября 2019

Другими словами, был ли какой-нибудь C ++, где компилятор не мог оптимизировать вызывающую функцию, основываясь на просмотре объявления без вызова функции, вызываемой?

Ответ на этот прямойвопрос нет. Но это само по себе сильно вводит в заблуждение.

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

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

Давайте возьмем классический случай vector<T> для T с поддержкой noexcept. Этот случай не быстрее, потому что компилятор видит то, что иначе было бы серией копий, и автоматически изменяет их на перемещения только потому, что конструктор перемещения равен noexcept. Это даже невозможно для компилятора;не допускается такая перестановка вашего кода, так как это будет обнаружимое изменение в зависимости от того, что делают ваши конструкторы копирования / перемещения.

Такие случаи становятся быстрее, потому что исходный кодреализация vector обнаруживает, что T не перемещается. Обнаружив этот случай, он затем вызовет совершенно другой путь к коду для перемешивания элементов vector вокруг. Код, вызывающий функцию без выбрасывания, стал быстрее, потому что вызывающий код оптимизировал сам , а не потому, что компилятор обнаружил что-либо.

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

...