在C ++中对for循环中的变量进行重新声明

时间:2012-09-10 12:13:07

标签: c++ for-loop declaration

当尝试为多个平台编译以下(简化)代码时,我发现它在某些方面失败了,即IBM的xlC_r。进一步的调查发现,它也没有在考虑和铿锵声。它使用g ++和Solaris的CC成功编译。

以下是代码:

int main()
{
    int a1[1];
    bool a2[1];

    for (int *it = a1, *end = a1+1; it != end; ++it) {
        //...
        bool *jt = a2, *end = a2+1;
        //...
    }
}

xlC_r错误:

"main.cpp", line 8.25: 1540-0400 (S) "end" has a conflicting declaration.
"main.cpp", line 6.25: 1540-0425 (I) "end" is defined on line 6 of "main.cpp".

clang错误:

main.cpp:8:25: error: redefinition of 'end' with a different type
        bool *jt = a2, *end = a2+1;
                        ^
main.cpp:6:25: note: previous definition is here
    for (int *it = a1, *end = a1+1; it != end; ++it) {
                        ^

comeau错误:

"ComeauTest.c", line 8: error: "end", declared in for-loop initialization, may not
          be redeclared in this scope
          bool *jt = a2, *end = a2+1;
                          ^

问题是为什么这是一个错误?

回顾2003年的标准,它说以下(6.5.3):

The for statement
    for ( for-init-statement; condition; expression ) statement
is equivalent to
    {
        for-init-statement;
        while ( condition ) {
            statement;
            expression;
        }
    }
except that names declared in the for-init-statement are in the same
declarative-region as those declared in condition

此处没有声明条件的名称。

此外,它说(6.5.1):

When the condition of a while statement is a declaration, the scope
of the variable that is declared extends from its point of declaration
(3.3.1) to the end of the while statement. A while statement of the form
    while (T t = x) statement
is equivalent to
    label:
    {
        T t = x;
        if (t) {
            statement;
            goto label;
        }
    }

同样,我不确定这是否相关,因为条件中没有声明。因此,考虑到6.5.3中的等效重写,我的代码应该与:

相同
int main()
{
    int a1[1];
    bool a2[1];

    {
        int *it = a1, *end = a1+1;
        while (it != end) {
            //...
            bool *jt = a2, *end = a2+1;
            //...
            ++it;
        }
    }
}

这显然会允许结束重新宣布。

5 个答案:

答案 0 :(得分:8)

标准有些含糊不清。您引用的代码等同于while循环意味着存在一个内部范围,其中循环内的声明可以隐藏条件中的声明;但标准也说(引用C ++ 11,因为我没有方便的C ++ 03):

  

6.4 / 2条件规则既适用于选择陈述,也适用于forwhile陈述

     

6.4 / 3如果在由条件控制的子语句的最外面的块中重新声明了名称,则重新声明该名称的声明是不正确的。

     

在for-init语句中声明的6.5.3 / 1名称与条件中声明的名称在同一声明区域中

它们之间暗示名称不能重新声明。

该语言的旧版本(1998年以前)将for-init语句中的声明放入循环外的声明区域。这意味着您的代码有效,但这不会:

for (int i = ...; ...; ...) {...}
for (int i = ...; ...; ...) {...}  // error: redeclaration of i

答案 1 :(得分:3)

我认为代码是正确的。 IMO,问题在于括号。请注意,for语句定义为:

  

for(for-init-statement; condition; expression)statement

循环体没有大括号,在使用复合语句时会添加它们。但是复合语句添加了自己的声明区域,因此内部声明不应与for-init-statement发生冲突。

下面的代码用clang和G ++编译好(注意双括号):

for (int *it = a1, *end = a1+1; it != end; ++it) {{
    //...
    bool *jt = a2, *end = a2+1;
    //...
}}

我的猜测是clang编译器尝试优化,好像循环被定义为:

  

for(for-init-statement; condition; expression) { statement-seq }

随着意义的改变:两个声明性区域融合在一起。

第二,即使它根本没有使用括号:

for (int x=0; ;)
    char x;

它应该正确编译。从C ++草案6.5,参数。 2:

  

iteration-statement中的子语句隐式定义了块范围。

因此char x;本身定义(隐式)块范围,并且不会发生冲突的声明。

答案 2 :(得分:0)

该标准的当前版本清楚如下:

  

6.5迭代语句[stmt.iter]

     

2 - 迭代语句中的子语句[例如,for循环]隐含地定义了每次循环进入和退出的块范围(3.3)。 / p>

C有类似的规则:

  

6.8.5迭代语句

     语义      

5 - 迭代语句是一个块,其范围是其范围的严格子集   封闭的。循环体也是一个块,其范围是范围的严格子集   迭代语句。

答案 3 :(得分:0)

一些通常较旧的编译器使得for循环中声明的变量在循环范围之外可见。

为了让所有编译器都使用更新(更好)的方式来表示这样的宏:

// In older compilers, variables declared in a for loop statement
// are in the scope of the code level right outside the for loop.
// Newer compilers very sensibly limit the scope to inside the
// loop only. For compilers which don't do this, we can spoof it
// with this macro:
#ifdef FOR_LOOP_VARS_NEED_LOCAL_SCOPE
   #define for if(0); else for
#endif

然后,对于具有较旧行为的每个编译器,定义FOR_LOOP_VARS_NEED_LOCAL_SCOPE。例如,以下是如何为MSVC< 8:

#ifdef _MSC_VER
   #if _MSC_VER < 1400   //  earlier than MSVC8
      #define FOR_LOOP_VARS_NEED_LOCAL_SCOPE
   #endif
#endif

答案 4 :(得分:0)

我在这里参加派对有点晚了,但我认为C ++ 11标准中的这段话最明显地禁止了这一点:

  

3.3.3块范围[basic.scope.local]

     

4 - 在for-init-statement,for-range-declaration 中声明的名称,以及if,while,for的条件,   和switch语句是if,while,for或switch语句的本地语句(包括受控语句)   声明),不得在该声明的后续条件下或在最外面的声明中重新声明   受控声明的块(或者,对于if语句,任何最外面的块);见6.4。