Почему я не могу поместить объявление переменной в тестовую часть цикла while? - PullRequest
18 голосов
/ 10 октября 2008

Очевидно, что вы можете поместить объявление переменной в цикл for:

for (int i = 0; ...

и я заметил, что вы можете сделать то же самое в операторах if и switch:

if ((int i = f()) != 0) ...

switch (int ch = stream.get()) ...

Но когда я пытаюсь сделать то же самое в цикле while:

while ((int ch = stream.get()) != -1) ...

Компилятору (VC ++ 9.0) он вообще не нравится.

Это совместимое поведение? Есть ли причина для этого?

РЕДАКТИРОВАТЬ : Я нашел, что могу сделать это:

while (int ch = stream.get() != -1) ...

но из-за правил приоритета это интерпретируется как:

while (int ch = (stream.get() != -1)) ...

это не то, что я хочу.

Ответы [ 6 ]

12 голосов
/ 10 октября 2008

Грамматика для условия в стандарте '03 определяется следующим образом:

condition:
  expression
  type-specifier-seq declarator = assignment-expression

Поэтому вышеизложенное допускает только такие условия, как:

if ( i && j && k ) {}
if ( (i = j) ==0 ) {}
if ( int i = j ) {}

Стандарт позволяет условию объявлять переменную, однако они сделали это, добавив новое грамматическое правило, называемое «условие», которое может быть выражением или декларатором с инициализатором. В результате то, что вы находитесь в состоянии if, for, while или switch, не означает, что вы можете объявить переменную внутри выражения.

11 голосов
/ 11 октября 2008

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

while () может иметь один из двух синтаксисов: while () или while (). Объявление использует «=» и выглядит как выражение, но это другая синтаксическая сущность.

Когда вы пишете

while(int i = 1) {
}

, это прекрасно. «int i = 1» является объявлением. Однако, то, что вы хотите, это

while ( (int i = 1) + 3) {
}

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

(после написания всей напыщенной речи я заметил, что 2 других человека написали то же самое. О, хорошо, чем больше, тем лучше)

10 голосов
/ 10 октября 2008

Это не соответствует правилам поведения. Часть 6.5.1.2 стандарта гласит:

Когда условием оператора while является объявление, область действия объявленной переменной расширяется от точки объявления (3.3.1) до конца оператора while. Некоторое утверждение формы

while (T t = x) оператор

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

label:
{ //start of condition scope
    T t = x;
    if (t) {
        statement
        goto label;
    }
}

Так что в вашем примере, ch должен быть объявлен в пределах цикла и работать правильно (с его повторным созданием через каждую итерацию цикла). Причина наблюдаемого поведения, скорее всего, связана с тем, что компилятор неправильно определил область действия переменной, а затем объявил ее несколько раз.

2 голосов
/ 10 октября 2008

Вы можете поместить объявление переменной в тестовое выражение цикла while. То, что вы не можете сделать, это поместить оператор объявления в другие выражения. Например, в выражении a + b + c нельзя заменить b на int i = f(). И то же самое верно для выражения (a); Вы не можете вставить int i=f(), чтобы получить выражение (int i=f()).

Итак, в while (int i = foo()) крайние скобки являются частью оператора while, а не текстового выражения, и все допустимо. В while ((int i = foo())) крайние скобки по-прежнему являются частью оператора while. Тестовое выражение будет иметь вид "(" expr ")", и в результате вы получите синтаксическую ошибку.

2 голосов
/ 10 октября 2008

Это может быть связано с тем, что содержимое предложения while оценивается в каждом цикле, поэтому он будет пытаться объявить "ch" несколько раз.

Приведенные вами примеры if, switch и для цикла будут иметь "ch", определяемый только один раз.

0 голосов
/ 10 октября 2008

Попробуйте Это не работает

while (int ch = stream.get(), ch != -1) ...

Я никогда не пробовал, но если комментарий в вашем редакторе правильный, это должно сработать.
VS 2005 даже не скомпилирует его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...