Почему try..catch блоки требуют скобки? - программирование
Подтвердить что ты не робот

Почему try..catch блоки требуют скобки?

В других операциях, например if... else, вы можете избежать привязок, если в блоке есть только одна команда, вы не можете сделать это с помощью try... catch блоков: компилятор не покупает его. Например:

try
    do_something_risky();
catch (...)
    std::cerr << "Blast!" << std::endl;

С приведенным выше кодом g++ просто говорит, что ожидает '{' перед do_something_risky(). Почему эта разница в поведении между try... catch и, скажем, if... else?

Спасибо!

4b9b3361

Ответ 1

Прямо из спецификации С++:

try-block:
    try compound-statement handler-seq

Как видите, все try-block ожидают a compound-statement. По определению составной оператор представляет собой несколько операторов, заключенных в фигурные скобки.

Все, что содержится в составном заявлении, обеспечивает создание новой области для блока try. Это также делает все немного легче читать по моему мнению.

Вы можете проверить это на странице 359 Спецификация языка С++

Ответ 2

Не знаю, почему, но одно преимущество в том, что проблема обвинчивания не существует. См. dangling-else для двусмысленности, которая может возникнуть, когда фигурные скобки необязательны.

Ответ 3

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

Итак, я думаю, авторы грамматики С++ просят авторов g++ (или любых стандартов, соответствующих компилятору С++) подготовить его к худшим возможным случаям, и авторы g++, похоже, сделали это.

Ответ 4

Хорошо, во-первых, как работает грамматика.

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

Ответ 5

Почему? Компромисс между безопасностью и обратной совместимостью.

Уроки, извлеченные из if... else, показали, что требуемые брекеты устраняют ошибки. Теперь, люди ISO С++ имеют сильное предпочтение обратной совместимости с C, поэтому они не изменяли синтаксис C для if... else. Но для новых конструкций требуются брекеты для демаркации контролируемых блоков, поскольку они не будут отображаться в старом C-коде, и поэтому обратная совместимость не является проблемой.

Ответ 6

Это как они хотели быть. Нет оправдания, это закон.

Ответ 7

Синтаксис try-блока:

try compound-statement handler-sequence

где последовательность-обработчик представляет собой последовательность одного или нескольких обработчиков, которые имеют следующий синтаксис:

catch (type-specifier-seq declarator) compound-statement
catch (...) compound-statement

Это отличается от других утверждений, таких как управляющие инструкции (if, while, for и т.д.). Синтаксис для них:

if (condition) statement-true else statement-false  
while (condition) statement
for (init-statement; condition; iteration_expression) statement
etc.

Теперь вопрос в том, почему составной оператор нужен в try-блоке вместо одного оператора?

Подумайте об этом коде:

int main()
{
  // before try-statement.

  try g(); catch (std::runtime_error e) handleError(e);

  // after try-statement.
}

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

Теперь подумайте о продолжительности хранения и связи "e". Что вы ожидаете, так это то, что "e" можно передать только перед вызовом функции handleError, но нет после завершения вызова. Он должен иметь автоматическую продолжительность хранения и отсутствие связи в этой "области". Вероятно, это могло бы быть сделано путем неявного определения локальной области видимости, как в других операторах, но сделать объявление-представление выглядит как параметр функции, вероятно, была лучшей идеей. Поэтому необходим блок (составной оператор). Se bellow.

Теперь подумайте о попытке и заявлении после этого. Нет причин использовать ключевое слово try there, и нет причин использовать составной оператор, но синтаксис может стать неоднозначным и сложным.

Вот что сказал об этом Страуструп в Обработка исключений для С++:

It might be possible to simplify the

try { ... } catch (abc) { ... }

syntax  by  removing  the  apparently  redundant try keyword,
removing  the  redundant  parentheses, and by allowing a handler
to be attached to any statement and not just to a block.  For 
example, one might allow:

void f()
{
  g(); catch (x1) { /* ... */ }
}

as an alternative to - 28 -

void f()
{
  try { g(); } catch (x1) { /* ... */ }
}

The added notational convenience seems insignificant and may not
even be convenient. People seem to prefer syntactic constructs that
start with a prefix that alerts them to what is going on, and it may
be easier to generate good code when the try keyword is required.  

И после более подробного объяснения:

Allowing exception handlers to be attached to blocks only and not to
simple statements simplifies syntax analysis (both for humans and
computers) where several exceptions are caught and where nested
exception  handlers are considered (see Appendix E). For example,
assuming that we  allowed handlers to be attached to any statement
we could write:

try try f(); catch (x) { ... } catch (y) { ... } catch (z) { ... }

The could be interpreted be in at least three ways:

try { try f(); catch (x) { ... } } catch (y) { ... } catch (z) { ... }
try { try f(); catch (x) { ... } catch (y) { ... } } catch (z) { ... }
try { try f(); catch (x) { ... } catch (y) { ... } catch (z) { ... } }

There seems to be no reason to allow these ambiguities even if there
is a trivial and systematic way for a parser to chose one
interpretation over another. Consequently, a { is required after a
try and a matching } before the first of the associated sequence of
catch clauses.

Как сказал Страуструп, без фигурных скобок утверждение может означать разные вещи в зависимости от правила, и вам, вероятно, придется поместить фигурные скобки, чтобы прояснить интенцию. Можем ли мы сделать некоторые, которые выглядят сложными с помощью if-утверждения, например, в примере Stroustrup? Конечно, мы можем, что-то вроде этого, например:

if (c1) if (c2) f(); else if (c3) g(); else h();

Это фактически эквивалентно:

if (c1) { if (c2) f(); else { if (c3) g(); else h(); } }

Но я думаю, что это менее проблематично, чем случай try-block. Для if-statament существует два синтаксиса:

if (condition) statement-true
if (condition) statement-true else statement-false

потому что имеет смысл не иногда выполнять какое-либо действие. Но это не имеет смысла в try-block без catch-clause. "Попробуй" можно опустить, но не практично, как сказал Страуструп, но предложение catch не может, если вы указали блок try. Кроме того, может быть более одного улова, связанного с одним и тем же блоком try, но только один выполняется на основе правил, которые зависят от типа исключения и порядка catch-clauses.

Теперь, если синтаксис if-else изменяется на:

if (condition) compound-statement-true else compound-statement-false

тогда вы должны написать if-else следующим образом:

if (c1) { f(); } else { if (c2) { g(); } else { h(); } }

Посмотрите, что ключевое слово 'elseif' отсутствует, нет специального синтаксиса для 'else if'. Я думаю, что даже защитники "положить скобки всегда" не любят писать вот так: вместо этого напишите:

if (c1) { f(); } else if (c2) { g(); } else { h(); }

Я думаю, что это не является сильной причиной для определения синтаксиса, как указано выше, и ввести в язык ключевое слово 'elseif' или определить специальный синтаксис для 'else if'.

Ответ 8

Не уверен, что вы используете .NET, но CLR использует фигурные скобки в качестве флагов.

http://dotnet.sys-con.com/node/44398

Из статьи: "Таблица SEH (обработка исключений структуры) состоит из набора предложений, описывающих структуру защищенного кода. В таблице есть набор двоичных флагов, описывающих тип предложения обработки исключений: флаг Try Offset, который является началом защищенного кодового блока; флаг Try Length, который является длиной защищенного кода; Флаги обработчика Offset и Handler Length, в которых подробно описывается начало блока обработчика исключений и его длину, а также флаг Token или Filter Offset, в зависимости от типа обработчика исключений, который был определен. Эта информация позволяет CLR определять, что делать, когда возникает исключение. Он отображает начало защищенного кодового блока, код для выполнения для исключения и специальную семантику, связанную с фильтрацией или другим особым обстоятельством.

Я бы предположил, что другие фреймворки делают то же самое.

Ответ 9

В основном это потому, что

if (a)
    int b = 10;
else 
    int b = 5;
b += 5;

Ошибка, потому что if... else without {} является ярлыком синтаксиса для этого

if (a) {
    int b = 10;
} else {
    int b = 5;
}
b += 5;

который явно сообщает вам, что int b находится в другой области действия, чем остальная часть программного обеспечения.

Если я не ошибаюсь, следующее также терпит неудачу

a ? int b = 10 : int b = 5;
b += 5;

Конечно, ваш компилятор может оптимизировать этот код для вас... но он должен технически сбой из-за областей в инструкции if/else.

Всякий раз, когда вы видите {}, вы определяете область действия программного обеспечения.

-Stephen