9 Statements [stmt.stmt]

Правила 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.

9.1 Labeled statement [stmt.label]

Заявление можно пометить.

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

Метки case и метки по умолчанию должны встречаться только в операторах switch.

9.2 Expression statement [stmt.expr]

Операторы выражения имеют вид

expression-statement:
	expressionopt ;

Выражение - это discarded-value expression. Все побочные эффекты от оператора выражения завершаются до выполнения следующего оператора. Оператор выражения с отсутствующим выражением называется a . [ Большинство операторов являются операторами-выражениями - обычно присваиваниями или вызовами функций. Нулевой оператор полезен для переноса метки непосредственно перед составным оператором и для передачи пустого тела в оператор итерации, такой как . ] null statementNote: } while statementend note

9.3 Compound statement or block [stmt.block]

Чтобы можно было использовать несколько операторов там, где ожидается один, предоставляется составной оператор (также, что эквивалентно, называемый «блок»).

compound-statement:
	{ statement-seqopt }
statement-seq:
	statement
	statement-seq statement

Составной оператор определяет block scope. [ Note: Объявление - это statement([stmt.dcl]). ]end note

9.4 Selection statements [stmt.select]

Операторы выбора выбирают один из нескольких потоков управления.

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-statementend note [stmt.stmt] substatement statementstatements selection-statement else if block scopecompound-statementExample:

if (x)
  int i;

можно эквивалентно переписать как

if (x) {
  int i;
}

Таким образом, после if заявления, i больше не входит в сферу действия. ]end example

9.4.1 The if statement [stmt.if]

Если условие ([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.

9.4.2 The switch statement [stmt.switch]

В switch заявлении причины управления должны быть переданы одному из нескольких операторов в зависимости от значения условия.

Условие должно быть целочисленного типа, типа перечисления или типа класса. Если тип класса, условие относится contextually implicitly converted к целочисленному или перечислимому типу. Если тип (возможно, преобразованный) подлежит integral promotions, условие преобразуется в повышенный тип. Любой оператор внутри switch оператора может быть помечен одной или несколькими метками case, как показано ниже:

case constant-expression :

где constant-expressionдолжно быть преобразованием constant expression настроенного типа состояния переключения. Никакие две константы case в одном переключателе не должны иметь одинаковое значение после преобразования.

Должно быть не более одной метки формы

default :

в switch заявлении.

Операторы переключения могут быть вложенными; 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.

9.5 Iteration statements [stmt.iter]

Операторы итерации определяют цикл.

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-statementend 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]

9.5.1 The while statement [stmt.while]

В 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

9.5.2 The do statement [stmt.do]

Выражение есть contextually converted to bool; если это преобразование сформировано неверно, значит, неверно сформирована программа.

В do операторе подзапрос выполняется многократно, пока значение выражения не станет равным false. Проверка проводится после каждого выполнения инструкции.

9.5.3 The for statement [stmt.for]

for заявление

for ( init-statement conditionopt ; expressionopt ) statement

эквивалентно

{
	init-statement
	while ( condition ) {
		statement
		expression ;
	}
}

за исключением того, что имена, объявленные в, init-statementнаходятся в той же декларативной области, что и имена, объявленные в condition, и за исключением того, что перед повторной оценкой будет выполняться continue in statement(не заключенный в другой оператор итерации) . [ Таким образом, первый оператор определяет инициализацию цикла; условие ( ) задает тест, выполняемый по порядку перед каждой итерацией, так что цикл завершается, когда условие становится ; выражение часто определяет приращение, которое выполняется после каждой итерации. ]expressionconditionNote: [stmt.select]falseend 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]

9.5.4 The range-based for statement [stmt.ranged]

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должен определять класс или перечисление.

9.6 Jump statements [stmt.jump]

Операторы прыжка безоговорочно передают управление.

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

9.6.1 The break statement [stmt.break]

break Заявление должно происходить только в или в заявлении и причинах прекращения наименьшего ограждающих или заявления; управление переходит к оператору, следующему за завершенным оператором, если таковой имеется.iteration-statement switch iteration-statementswitch

9.6.2 The continue statement [stmt.cont]

Этот continue оператор должен встречаться только в и заставляет управление переходить к части продолжения цикла наименьшего охватывающего элемента , то есть к концу цикла. Точнее, в каждом из утвержденийiteration-statementiteration-statement

while (foo) {
  {
    // ...
  }
contin: ;
}
do {
  {
    // ...
  }
contin: ;
} while (foo);
for (;;) {
  {
    // ...
  }
contin: ;
}

a, continue не содержащийся во вложенном операторе итерации, эквивалентен goto contin.

9.6.3 The return statement [stmt.return]

Функция возвращается к вызывающему с помощью return оператора.

Оператор 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 noteExample:

std::pair<std::string,int> f(const char* p, int x) {
  return {p,x};
}

end example] Выход за конец конструктора, деструктора или функции с cv void возвращаемым типом эквивалентен a return без операнда. В противном случае переход за пределы функции, отличной от main результата, приведет к неопределенному поведению.

Копирование-инициализация результата вызова упорядочивается до уничтожения временных файлов в конце полного выражения, установленного операндом оператора return, который, в свою очередь, упорядочивается до уничтожения локальных переменных ([stmt.jump]) блок, содержащий оператор возврата.

9.6.4 The goto statement [stmt.goto]

В goto безоговорочном передает управляющий оператор в оператор , помеченный идентификатором. Идентификатор должен находиться label в текущей функции.

9.7 Declaration statement [stmt.dcl]

Оператор объявления вводит в блок один или несколько новых идентификаторов; это имеет форму

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 метке считается скачком в этом отношении.

Реализация не должна создавать взаимоблокировки при выполнении инициализатора. Тупики по-прежнему могут быть вызваны логикой программы; реализации нужно только избегать взаимоблокировок из-за собственных операций синхронизации.

9.8 Ambiguity resolution [stmt.ambig]

В грамматике есть двусмысленность, связанная с 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 noteExample:

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]