Если не указано иное, операторы выполняются последовательно.
statement: labeled-statement attribute-specifier-seqopt expression-statement attribute-specifier-seqopt compound-statement attribute-specifier-seqopt selection-statement attribute-specifier-seqopt iteration-statement attribute-specifier-seqopt jump-statement declaration-statement attribute-specifier-seqopt try-block init-statement: expression-statement simple-declaration condition: expression attribute-specifier-seqopt decl-specifier-seq declarator brace-or-equal-initializer
Необязательный параметр attribute-specifier-seqотносится к соответствующему утверждению.
Правила conditions применяются как к selection-statements и к for и while отчетности ([stmt.iter]). Не declaratorдолжен указывать функцию или массив. Не decl-specifier-seqдолжен определять класс или перечисление. Если появляется в , тип объявляемого идентификатора выводится из инициализатора, как описано в . auto type-specifierdecl-specifier-seq [dcl.spec.auto]
Имя, введенное объявлением в a condition(введенное либо посредством, decl-specifier-seqлибо declaratorиз условия), находится в области от точки объявления до конца подзаполнений, контролируемых условием. Если имя повторно объявляется во внешнем блоке подзапроса, контролируемого условием, объявление, которое повторно объявляет имя, имеет неправильный формат. [ Example:
if (int x = f()) { int x; // ill-formed, redeclaration of x } else { int x; // ill-formed, redeclaration of x }
— end example ]
Значение a, conditionкоторое является инициализированным объявлением в операторе, отличном от switch оператора, является значением объявленной переменной contextually converted to bool. Если это преобразование сформировано неверно, значит, неверно сформирована программа. Значение a, conditionкоторое является инициализированным объявлением в switch операторе, является значением объявленной переменной, если она имеет целочисленный или перечисляемый тип, или этой переменной, неявно преобразованной в целочисленный или перечисляемый тип в противном случае. Значение conditionвыражения, которое является выражением, является значением выражения, контекстно преобразованным в bool операторы for, отличные от switch; если это преобразование сформировано неверно, значит, неверно сформирована программа. Значение условия будет называться просто «условием», при котором использование однозначно.
Если a conditionможет быть синтаксически разрешено либо как выражение, либо как объявление имени области блока, оно интерпретируется как объявление.
В decl-specifier-seqa conditionкаждый decl-specifierдолжен быть либо a, type-specifier либо constexpr.
labeled-statement: attribute-specifier-seqopt identifier : statement attribute-specifier-seqopt case constant-expression : statement attribute-specifier-seqopt default : statement
Необязательный параметр attribute-specifier-seqотносится к этикетке. identifier label Объявляет идентификатор. Единственное использование метки идентификатора - это цель файла goto. Объем метки - это функция, в которой она появляется. Ярлыки не должны повторно объявляться внутри функции. Метку можно использовать в goto операторе перед его объявлением. Ярлыки имеют собственное пространство имен и не мешают другим идентификаторам. [ Note: Метка может иметь то же имя, что и другое объявление в той же области видимости или template-parameterиз охватывающей области. Unqualified name lookup игнорирует ярлыки. ] — end note
Операторы выражения имеют вид
expression-statement: expressionopt ;
Выражение - это discarded-value expression. Все побочные эффекты от оператора выражения завершаются до выполнения следующего оператора. Оператор выражения с отсутствующим выражением называется a . [ Большинство операторов являются операторами-выражениями - обычно присваиваниями или вызовами функций. Нулевой оператор полезен для переноса метки непосредственно перед составным оператором и для передачи пустого тела в оператор итерации, такой как . ] null statement Note: } while statement — end note
Чтобы можно было использовать несколько операторов там, где ожидается один, предоставляется составной оператор (также, что эквивалентно, называемый «блок»).
compound-statement: { statement-seqopt }
statement-seq: statement statement-seq statement
Составной оператор определяет block scope. [ Note: Объявление - это statement([stmt.dcl]). ] — end note
Операторы выбора выбирают один из нескольких потоков управления.
selection-statement: if constexpropt ( init-statementopt condition ) statement if constexpropt ( init-statementopt condition ) statement else statement switch ( init-statementopt condition ) statement
См [dcl.meaning] для опциональных attribute-specifier-seqв состоянии. [ Заканчивается точкой с запятой. ] В разделе термин относится к содержащимся или появляющимся в нотации синтаксиса. Подложка в (каждая подгруппа в форме оператора) неявно определяет a . Если подзапрос в операторе выбора является единственным оператором, а не является , это как если бы оно было переписано как составное-выражение, содержащее исходное подзаголовок. [ Note: init-statement — end note [stmt.stmt] substatement statementstatements selection-statement else if block scopecompound-statement Example:
if (x) int i;
можно эквивалентно переписать как
if (x) { int i; }
Таким образом, после if заявления, i больше не входит в сферу действия. ] — end example
Если условие ([stmt.select]) приводит true к выполнению первого подзапуска. Если else часть оператора выбора присутствует и условие дает результат false, выполняется второе подзапрос. Если первая подгруппа достигается через метку, условие не оценивается, и вторая подгруппа не выполняется. Во второй форме if утверждения (включая ту, которая включает в себя else), если первое подзаполнение также является if утверждением, то это внутреннее if утверждение должно содержать else часть.90
Если if заявление имеет форму if constexpr, значение условия должно быть a contextually converted constant expression of type bool; эта форма называется constexpr if заявлением. Если значение преобразованного условия равно false, первая подгруппа - это a discarded statement, в противном случае вторая подгруппа, если она присутствует, является отвергнутым оператором. Во время создания включающего templated объекта, если условие не зависит от значения после его создания, отброшенное подзаполнение (если оно есть) не создается. [ Note: Odr-uses в отвергнутом заявлении не требуют определения сущности. ] Знак или метка, появляющаяся в таком операторе, должны быть связаны с элементом в том же операторе. A, объявленный в подвыложении constexpr if, должен ссылаться только на a в том же подзапросе. [ — end note case default if switch statement if label statement Example:
template<typename T, typename ... Rest> void g(T&& p, Rest&& ...rs) { // ... handle p if constexpr (sizeof...(rs) > 0) g(rs...); // never instantiated with an empty argument list } extern int x; // no definition of x required int f() { if constexpr (true) return 0; else if (x) return x; else return -x; }
— end example ]
if Утверждение вида
if constexpropt ( init-statement condition ) statement
эквивалентно
{ init-statement if constexpropt ( condition ) statement }
и if выписка по форме
if constexpropt ( init-statement condition ) statement else statement
эквивалентно
{ init-statement if constexpropt ( condition ) statement else statement }
за исключением того, что имена, объявленные в, init-statementнаходятся в той же декларативной области, что и имена, объявленные в condition.
Другими словами, else связан с ближайшим не-elsed if.
В switch заявлении причины управления должны быть переданы одному из нескольких операторов в зависимости от значения условия.
Условие должно быть целочисленного типа, типа перечисления или типа класса. Если тип класса, условие относится contextually implicitly converted к целочисленному или перечислимому типу. Если тип (возможно, преобразованный) подлежит integral promotions, условие преобразуется в повышенный тип. Любой оператор внутри switch оператора может быть помечен одной или несколькими метками case, как показано ниже:
case constant-expression :
где constant-expressionдолжно быть преобразованием constant expression настроенного типа состояния переключения. Никакие две константы case в одном переключателе не должны иметь одинаковое значение после преобразования.
Операторы переключения могут быть вложенными; case или default метка связана с наименьшим переключатель вмещающих его.
Когда switch оператор выполняется, его состояние оценивается и сравнивается с каждой константой case. Если одна из констант case равна значению условия, управление передается оператору, следующему за меткой сопоставленного case. Если ни одна константа регистра не соответствует условию и если есть default метка, управление передается оператору, помеченному меткой по умолчанию. Если ни один регистр не соответствует и если нет, default то ни один из операторов в переключателе не выполняется.
case и default сами по себе ярлыки не изменяют поток управления, который продолжается беспрепятственно через такие ярлыки. Чтобы выйти из переключателя, см break. [ Note: Обычно подоператор , что является предметом переключателя является соединением и case и default этикетки появляются на показаниях верхнего уровня , содержащихся в (соединение) подоператор, но это не является обязательным. Объявления могут появляться в подзапросе switch утверждения. ] — end note
switch Утверждение вида
switch ( init-statement condition ) statement
эквивалентно
{ init-statement switch ( condition ) statement }
за исключением того, что имена, объявленные в, init-statementнаходятся в той же декларативной области, что и имена, объявленные в condition.
Операторы итерации определяют цикл.
iteration-statement: while ( condition ) statement do statement while ( expression ) ; for ( init-statement conditionopt ; expressionopt ) statement for ( for-range-declaration : for-range-initializer ) statement
for-range-declaration: attribute-specifier-seqopt decl-specifier-seq declarator attribute-specifier-seqopt decl-specifier-seq ref-qualifieropt [ identifier-list ]
for-range-initializer: expr-or-braced-init-list
См [dcl.meaning] для дополнительного attribute-specifier-seqв for-range-declaration. [ Заканчивается точкой с запятой. ] Note: init-statement — end note
Подстрока в iteration-statementнеявно определяет блок, scope который вводится и выходит каждый раз в цикле.
Если подзапрос в операторе итерации является единственным оператором, а не a compound-statement, это как если бы оно было переписано как составное выражение, содержащее исходное выражение. [ Example:
while (--x >= 0) int i;
можно эквивалентно переписать как
while (--x >= 0) { int i; }
Таким образом, после while заявления, i больше не входит в сферу действия. ] — end example
Если имя, введенное в init-statementили for-range-declaration повторно объявлено во внешнем блоке подзапуска, программа имеет неправильный формат . [ Example:
void f() { for (int i = 0; i < 10; ++i) int i = 0; // error: redeclaration for (int i : { 1, 2, 3 }) int i = 1; // error: redeclaration }
— end example ]
В while операторе подзапрос выполняется многократно, пока значение условия ([stmt.select]) не станет равнымfalse. Проверка проводится перед каждым выполнением подзапуска.
Когда условием while оператора является объявление, область объявляемой переменной простирается от нее point of declaration до конца . Утверждение вида while statement while
while (T t = x) statement
эквивалентно
label: { // start of condition scope T t = x; if (t) { statement goto label; } } // end of condition scope
Переменная, созданная в условии, уничтожается и создается при каждой итерации цикла. [ Example:
struct A {
int val;
A(int i) : val(i) { }
~A() { }
operator bool() { return val != 0; }
};
int i = 1;
while (A a = i) {
// ...
i = 0;
}
В цикле while конструктор и деструктор вызываются дважды: один раз для успешного условия и один раз для невыполненного условия. ] — end example
Выражение есть contextually converted to bool; если это преобразование сформировано неверно, значит, неверно сформирована программа.
for заявление
for ( init-statement conditionopt ; expressionopt ) statement
эквивалентно
{ init-statement while ( condition ) { statement expression ; } }
за исключением того, что имена, объявленные в, init-statementнаходятся в той же декларативной области, что и имена, объявленные в condition, и за исключением того, что перед повторной оценкой будет выполняться continue in statement(не заключенный в другой оператор итерации) . [ Таким образом, первый оператор определяет инициализацию цикла; условие ( ) задает тест, выполняемый по порядку перед каждой итерацией, так что цикл завершается, когда условие становится ; выражение часто определяет приращение, которое выполняется после каждой итерации. ]expressioncondition Note: [stmt.select]false — end note
Один или оба из condition и expressionможно опустить. Отсутствие condition делает подразумеваемое while предложение эквивалентным while(true).
Если init-statementэто объявление, объем объявленного имени (имен) расширяется до конца for оператора. [ Example:
int i = 42;
int a[10];
for (int i = 0; i < 10; i++)
a[i] = i;
int j = i; // j = 42
— end example ]
for Заявление на основе диапазона
for ( for-range-declaration : for-range-initializer ) statement
эквивалентно
{ auto &&__range = for-range-initializer ; auto __begin = begin-expr ; auto __end = end-expr ; for ( ; __begin != __end; ++__begin ) { for-range-declaration = *__begin; statement } }
куда
если for-range-initializerесть expression, он рассматривается, как если бы он был заключен в круглые скобки (так что оператор запятой не может быть интерпретирован как разделяющий два init-declarators);
__range, __beginи __end - переменные, определенные только для демонстрации; а также
begin-expr и end-expr определяются следующим образом:
если for-range-initializer- выражение типа массива R, begin-expr и end-expr - __range и __range + __bound, соответственно, где __bound - граница массива. Если R это массив с неизвестной границей или массив неполного типа, программа имеет неправильный формат;
если for-range-initializerявляется выражением типа класса C, unqualified-ids begin и end ищутся в области видимости, C как если бы by class member access lookup, и если один из них (или оба) находит по крайней мере одно объявление, begin-expr и end-expr являются __range.begin() и __range.end(), соответственно;
в противном случае begin-expr и end-expr являются begin(__range) и end(__range), соответственно, где begin и end ищутся в associated namespaces. [ Note: Обычный unqualified lookup не выполняется. ] — end note
[ Example:
int array[5] = { 1, 2, 3, 4, 5 }; for (int& x : array) x *= 2;
— end example ]
В decl-specifier-seqa for-range-declarationкаждый decl-specifierдолжен быть либо a, type-specifier либо constexpr. Не decl-specifier-seqдолжен определять класс или перечисление.
Операторы прыжка безоговорочно передают управление.
jump-statement: break ; continue ; return expr-or-braced-init-listopt ; goto identifier ;
При выходе из области видимости ( automatic storage duration независимо от ее выполнения) объекты , которые были созданы в этой области, уничтожаются в порядке, обратном их построению. [ Note: Для временных см [class.temporary].. ] Передача из цикла, из блока или обратно через инициализированную переменную с автоматической продолжительностью хранения включает в себя уничтожение объектов с автоматической продолжительностью хранения, которые находятся в области видимости в точке передачи, но не в точке передачи. (См. Переводы в блоки). [ Однако программа может быть завершена (например, путем вызова или ( )) без разрушения объектов класса с автоматической продолжительностью хранения. ] — end note [stmt.dcl] Note: std::exit() std::abort() [support.start.term] — end note
break Заявление должно происходить только в или в заявлении и причинах прекращения наименьшего ограждающих или заявления; управление переходит к оператору, следующему за завершенным оператором, если таковой имеется.iteration-statement switch iteration-statementswitch
Этот continue оператор должен встречаться только в и заставляет управление переходить к части продолжения цикла наименьшего охватывающего элемента , то есть к концу цикла. Точнее, в каждом из утвержденийiteration-statementiteration-statement
while (foo) {
{
// ...
}
contin: ;
}
do {
{
// ...
}
contin: ;
} while (foo);
for (;;) {
{
// ...
}
contin: ;
}
a, continue не содержащийся во вложенном операторе итерации, эквивалентен goto contin.
Оператор expr-or-braced-init-list возврата называется его операндом. Оператор возврата без операнда должен использоваться только в функции, тип возврата которой - cv voida constructorили a destructor. Оператор возврата с операндом типа должен использоваться только в функции, тип возврата которой равен . Оператор возврата с любым другим операндом должен использоваться только в функции, тип возврата которой не является ; оператор return инициализирует результат glvalue или объект результата prvalue вызова функции (явного или неявного) из операнда. [ Оператор возврата может включать вызов конструктора для выполнения копирования или перемещения операнда, если он не является значением prvalue или если его тип отличается от типа возврата функции. Операция копирования, связанная с оператором return, может быть исключена или преобразована в операцию перемещения, если возвращается автоматическая переменная продолжительности хранения ( ). ] [ void cv void cv void copy-initialization Note: [class.copy] — end note Example:
std::pair<std::string,int> f(const char* p, int x) { return {p,x}; }
— end example ] Выход за конец конструктора, деструктора или функции с cv void возвращаемым типом эквивалентен a return без операнда. В противном случае переход за пределы функции, отличной от main результата, приведет к неопределенному поведению.
Копирование-инициализация результата вызова упорядочивается до уничтожения временных файлов в конце полного выражения, установленного операндом оператора return, который, в свою очередь, упорядочивается до уничтожения локальных переменных ([stmt.jump]) блок, содержащий оператор возврата.
Оператор объявления вводит в блок один или несколько новых идентификаторов; это имеет форму
declaration-statement: block-declaration
Если идентификатор, введенный объявлением, был ранее объявлен во внешнем блоке, внешнее объявление скрывается до конца блока, после чего оно возобновляет свою силу.
Переменные с automatic storage duration инициализируются каждый раз при их declaration-statementвыполнении. Переменные с автоматической продолжительностью хранения, объявленные в блоке, уничтожаются при выходе из блока ([stmt.jump]).
Можно передать в блок, но не в обход объявлений с инициализацией. Программа, которая перескакивает91 из точки, где переменная с автоматической продолжительностью хранения не находится в области видимости, к точке, где она находится в области видимости, плохо сформирована, если только переменная не имеет скалярный тип, тип класса с тривиальным конструктором по умолчанию и тривиальный деструктор, a cv-квалифицированная версия одного из этих типов или массив одного из предыдущих типов, объявленный без расширения initializer. [ Example:
void f() { // ... goto lx; // ill-formed: jump into scope of a // ... ly: X a = 1; // ... lx: goto ly; // OK, jump implies destructor call for a followed by // construction again immediately following label ly }
— end example ]
Динамическая инициализация переменной области блока с помощью static storage duration или thread storage duration выполняется при первом прохождении управления через ее объявление; такая переменная считается инициализированной по завершении ее инициализации. Если инициализация завершается выдачей исключения, инициализация не завершена, поэтому она будет повторена, когда в следующий раз элемент управления войдет в объявление. Если элемент управления входит в объявление одновременно во время инициализации переменной, параллельное выполнение должно дождаться завершения инициализации.92 Если элемент управления повторно входит в объявление рекурсивно во время инициализации переменной, поведение не определено. [ Example:
int foo(int i) {
static int s = foo(2*i); // recursive call - undefined
return i+1;
}
— end example ]
Деструктор для объекта блочной области со статической продолжительностью хранения или продолжительностью хранения потока будет выполняться тогда и только тогда, когда он был создан. [ Note: [basic.start.term] описывает порядок, в котором уничтожаются объекты блочной области со статической продолжительностью хранения и продолжительностью хранения потоков. ] — end note
Переход от условия switch оператора к case метке считается скачком в этом отношении.
Реализация не должна создавать взаимоблокировки при выполнении инициализатора. Тупики по-прежнему могут быть вызваны логикой программы; реализации нужно только избегать взаимоблокировок из-за собственных операций синхронизации.
В грамматике есть двусмысленность, связанная с expression-statements and declarations: An expression-statementс a в function-style explicit type conversion качестве крайнего левого подвыражения может быть неотличимо от a, declarationгде первое declaratorначинается с a (. В этих случаях используется statementфайл declaration.
[ Note: Если statementсинтаксически не может быть a declaration, двусмысленности нет, поэтому это правило не применяется. Вся statementпотребность Мощь быть исследованы , чтобы определить , является ли этот случай. Это решает смысл многих примеров. [ Example: Предполагая , что T это simple-type-specifier,
T(a)->m = 7; // expression-statement T(a)++; // expression-statement T(a,5)<<c; // expression-statement T(*d)(int); // declaration T(e)[5]; // declaration T(f) = { 1, 2 }; // declaration T(*g)(double(3)); // declaration
В последнем примере выше, gкоторый является указателем на T, инициализируется на double(3). Это, конечно, плохо сформировано по семантическим причинам, но это не влияет на синтаксический анализ. ] — end example
Остальные случаи есть declarations. [ Example:
class T { // ... public: T(); T(int); T(int, int); }; T(a); // declaration T(*b)(); // declaration T(c)=7; // declaration T(d),e,f=3; // declaration extern int h; T(g)(h,2); // declaration
— end example ] ] — end note
Устранение неоднозначности чисто синтаксическое; то есть значение имен, встречающихся в таком утверждении, вне зависимости от того, есть они type-names или нет, обычно не используется и не изменяется в результате устранения неоднозначности. Шаблоны классов создаются по мере необходимости, чтобы определить, является ли полное имя type-name. Устранение неоднозначности предшествует синтаксическому анализу, и утверждение, устраняемое как декларация, может быть неверно сформированной декларацией. Если во время синтаксического анализа имя в параметре шаблона связано иначе, чем оно было бы связано во время пробного синтаксического анализа, программа имеет неправильный формат. Диагностика не требуется. [ Note: Это может произойти только в том случае, если имя объявлено ранее в объявлении. ] [ — end note Example:
struct T1 { T1 operator()(int x) { return T1(x); } int operator=(int x) { return x; } T1(int) { } }; struct T2 { T2(int){ } }; int a, (*(*b)(T2))(int), c, d; void f() { // disambiguation requires this to be parsed as a declaration: T1(a) = 3, T2(4), // T2 will be declared as a variable of type T1, but this will not (*(*b)(T2(c)))(int(d)); // allow the last part of the declaration to parse properly, // since it depends on T2 being a type-name }
— end example ]