为什么不能在while循环的测试部分放置变量声明?

21

显然,你可以在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)) ...

这并不是我想要的。


gcc实际上也不允许我在“if”语句中声明变量。 - Alex B
在上一个例子里,你的意思不是应该用 while (int ch = (stream.get() != -1)) 吗? - Steve Fallows
2
作为解决方案,您可以考虑使用逗号运算符:while (int ch, (ch = stream.get()) != -1) {//在此处执行操作} - workmad3
@Steve - 是的,当然。已经相应地进行了编辑。 - Ferruccio
在C++17中,对于whileifswitch可能是可行的。 - Mark K Cowan
1
@MarkKCowan 仅适用于 ifswitch。另请参见 为什么没有“带初始化的while语句”? - manlio
6个回答

16
问题在于,标准允许您在括号内进行声明。您想要做的是将声明作为表达式的一部分,这是标准不允许您执行的操作。
while()可以有两种语法:while(<declaration>)或while(<expression>)。声明使用"=",看起来像表达式,但它是一个不同的语法实体。
当您编写时
while(int i = 1) {
}

,那很好。"int i=1"是一种声明。然而,你想要的是


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

这是一个非常不同的东西。您想要在while()内部使用一个表达式,其中表达式的一个术语是声明。现在,声明是一种语句,因此不能成为表达式的一部分。这就是为什么您需要完成的事情无法完成的原因。

(在写完整个抱怨之后,我注意到还有其他两个人写了同样的话。噢,越多越好。)


14

在'03标准中,条件语句的语法定义如下:

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

因此,上述内容仅允许类似于以下条件:

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

这个标准允许在条件语句中声明变量,但是它们通过添加一个名为“condition”的新语法规则来实现,该规则可以是表达式或具有初始化程序的声明符。结果是,仅仅因为你在if、for、while或switch的条件语句中,并不意味着你可以在表达式中声明变量。


2
对的 - 关键是声明不是表达式。 - Michael Burr

11

这似乎不符合规范行为。标准的第6.5.1.2部分规定:

当while语句的条件是一个声明时,被声明的变量的作用域从其声明点(3.3.1)延伸到while语句的结尾。形式为

while (T t = x) statement

的while语句等同于

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

所以在你的例子中,ch应该在循环的作用域内声明,并且能够正确工作(它将在每个循环迭代中重新创建)。观察到的行为原因很可能是由于编译器没有正确地限定变量的作用域,然后多次声明它。


编译器完全符合要求,无法完成问题所要求的操作。请查看下面的答案。 - user3458

2

可能是因为while循环的内容在每次循环时都会被评估,因此它会尝试多次声明“ch”。

你提供的if、switch和for循环示例都只定义了一次“ch”。


2
你可以在while循环的测试表达式中放置变量声明。但你不能在其他表达式中放置声明语句。例如,在表达式a+b+c中,你不能用int i = f()替换b。对于表达式(a)也是一样的;你不能插入int i=f()来得到一个表达式(int i=f())
因此,在while (int i = foo())中,最外层的括号是while语句的一部分,而不是文本表达式的一部分,这是合法的。在while ((int i = foo()))中,最外层的括号仍然是while语句的一部分。测试表达式将具有"(" expr ")"的形式,你最终会得到一个语法错误。

我不理解 In while ((int i = foo())),最外层的括号仍然是 while 语句的一部分。测试表达式的形式将会是 ("(" expr ")"),这样就会导致语法错误。可以举一个更好的例子来解释最后一句话吗?谢谢 :) - Mr.Anubis
我理解的是我可以在while中声明变量x,例如while(int x=...),但初始化部分即...必须包含表达式而不是任何声明,对吗? - Mr.Anubis
1
@Mr.Anubis:你不能使用(( )),因为内部的()本身就是一个表达式,而你不能在其中声明变量。外部的()属于while循环,并不构成一个表达式。是的,x的初始化器必须是一个有效的表达式本身,不能包含第二个声明。 - MSalters

0

尝试 这不起作用

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

我从未尝试过,但如果您编辑中的注释是正确的,这应该可以工作。
VS 2005甚至无法编译它。


这段代码被编译器解释为:int ch = (stream.get(), ch != -1) - Richard Corden
根据我的消息来源,逗号运算符位于优先级的最低处 - 但我的结构还有其他问题。 - Mark Ransom
因为逗号运算符不允许声明语句出现在其中。但是赋值语句可以工作 int i; while(i = 0, ++i); 参见[expr.comma]。 - Yola

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接