C++中for循环中变量的重新声明
Redeclaration of a variable in a for-loop in C++
当尝试为多个平台编译以下(简化)代码时,我发现它在某些平台上失败了,即IBM的xlC_r。进一步的调查发现,它在comeau和clang上也失败了。它成功地与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".
叮当声错误:
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;
}
}
}
很明显,这将允许重新宣布结束。
标准有些模糊。您引用的等效于while
循环的代码意味着存在一个内部作用域,循环内的声明可以隐藏条件中的声明;然而,标准也说(引用C++11,因为我手头没有C++03):
6.4/2条件规则既适用于选择语句,也适用于
for
和while
语句6.4/3如果在由条件控制的子语句的最外层块中重新声明了名称,则重新声明名称的声明格式错误。
6.5.3/1在for init语句中声明的名称与在条件中声明的那些名称位于相同的声明性区域中
这意味着这些名字不能重新申报。
该语言的旧版本(1998年以前)将for-init语句中的声明放入循环外的声明性区域。这意味着你的代码是有效的,但这不是:
for (int i = ...; ...; ...) {...}
for (int i = ...; ...; ...) {...} // error: redeclaration of i
我认为代码是正确的。国际海事组织,问题在于背带。注意,for语句定义为:
for(for init语句;条件;表达式)语句
循环体没有大括号,它们是在使用复合语句时添加的。但是复合语句添加了自己的声明性区域,所以内部声明不应该与for-init-statement
冲突。
以下代码用clang和G++编译ok(注意双大括号):
for (int *it = a1, *end = a1+1; it != end; ++it) {{
//...
bool *jt = a2, *end = a2+1;
//...
}}
我的猜测是,clang编译器试图优化,就好像循环被定义为:
for(用于init语句;条件;表达式){语句seq}
随着意义的细微变化:两个声明性区域融合在一起。
然而,在第二个问题上,即使是它,也根本不使用括号:
for (int x=0; ;)
char x;
它应该正确编译。来自C++草案6.5,第2段:
迭代语句中的子语句隐式定义了块作用域。
因此,char x;
本身(隐式)定义了一个块范围,不应该发生冲突声明。
我来这里有点晚了,但我认为这在C++11标准中的这段话中是最明显的:
3.3.3区块范围【basic.scope.local】
4-在for init语句、for range声明和if条件下声明的名称,while,for,和switch语句是if、while、for或switch语句的本地语句(包括受控语句),并且不应在该语句的后续条件中重新声明,也不应在最外层中重新声明受控语句的块(或者,对于if语句,任何最外层的块);参见6.4。
标准的当前版本对此很清楚:
6.5迭代语句[stmt.iter]
2-迭代语句[例如,
for
循环]中的子语句隐含地定义了每次通过循环输入和退出的块范围(3.3)。
C有一个类似的规则:
6.8.5迭代语句
语义学5-迭代语句是一个块,其作用域是其作用域的严格子集封闭块。循环体也是一个块,其作用域是作用域的严格子集迭代语句的。
一些通常较旧的编译器使在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
- 我们可以在没有新实例化的情况下声明一个抽象方法来返回抽象超类中的子类对象吗
- 新运算符分配的大小大于声明的大小.为什么
- 为什么我不能只用前向声明 c++ 声明类的静态成员?
- 垫子声明:声明尺寸和大小()不同
- 如何从构造函数声明新的私有变量?
- 使用具有新信号槽语法的Qt插件系统在接口类中声明信号
- 使用 basic_string 声明新类型的字符串
- 声明数组时出现 SIGSEGV 错误,而不创建新的 int[size]
- STD :: bad_alloc在声明新的int [n]时
- 了解函数错误的歧义新声明
- 在C++中,如何在没有新元素且不单独声明单个元素的情况下创建"std::initializer_list<base *>"?
- 使用模板在类中声明新函数
- 元编程:动态声明一个新结构
- 使用括号会在声明新节点时会产生错误
- QT应用程序声明新对象后崩溃
- 新声明在使用delete时包含垃圾值和堆损坏
- 为什么新运算符没有声明为 [[nodiscard]]?
- 您只是调用前方声明和原型声明“声明”
- C++:友元声明'声明一个非模板函数
- C++在新函数声明器语法中访问此