Синтаксис try-block:
try compound-statement handler-sequence
где handler-sequence - это последовательность одного или нескольких обработчиков, которые имеют следующий синтаксис:
catch (type-specifier-seq declarator) compound-statement
catch (...) compound-statement
Это отличается от других операторов, таких как операторы управления (если, 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 ниже.
Теперь подумайте о попытке и утверждении после этого. Там нет никаких оснований для использования ключевого слова try и нет причин для использования составного оператора, но синтаксис может стать двусмысленным и сложным.
Вот что сказал об этом Страуструп в Обработка исключений для C ++ :
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, как в примере Страуструпа? Конечно, мы можем, что-то вроде этого, например:
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-утверждения:
if (condition) statement-true
if (condition) statement-true else statement-false
потому что иногда имеет смысл не делать других действий. Но нет смысла в блоке try без предложения catch. «Пытаться» можно опустить, но это непрактично, как сказал Страуструп, но предложение catch нельзя, если вы указали блок try. Помимо этого, может быть более одного улова, связанного с одним и тем же блоком try, но только один выполняется на основе правил, которые зависят от типа исключения и порядка выражений catch.
Теперь, что если синтаксис 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 если.