在另一个问题中,我刚刚发现了这个 C 智慧的小珍珠:
#define for if (false) {} else for
导致MSVC为一个非常有效的语句吐出“常量表达式”警告:
for (int i = 0; i <= 10; i++) {...}
我理解为什么 MSVC抱怨,因为它扩展为:
if (false) {} else for (int i = 0; i <= 10; i++) {...}
我只是不明白为什么开发人员会使用那个小片段。有人有想法吗?
答案 0 :(得分:92)
修复旧版Visual C ++(v6.0及更早版本)中的错误。在过去,Visual C ++已经破坏了关于在for
语句中声明的变量的范围规则:
// This compiles in old versions of Visual C++, but it is in fact INVALID C++
for(int i = 0; ...)
{
...
}
for(i = 0; ...)
{
}
换句话说,Visual C ++为i
提供了一个范围,好像它是在循环之外声明的,并且它允许您在循环完成后继续使用它。这导致代码如上面的代码片段。在更符合标准的编译器中,i
不再适用于第二个for
循环的定义,因此编译器会发出关于i
未定义的错误。
为了解决这个问题,有些人使用了这个宏(或非常相似的等效宏):
#define for if(0) {} else for
这会将for
循环更改为:
if(0)
{
}
else
for(int i = 0; ...)
{
...
}
这会将for
循环放入一个额外的范围级别,这样无论Visual C ++的错误如何,for
循环中声明的任何变量都将超出范围。这可以确保相同的代码在Visual C ++和符合标准的编译器中一致地正确编译,并且错误的代码无法正确编译。
另请注意,如果宏被定义为:
// DO NOT USE
#define for if(1) for
然后虽然这会对一些简单的代码产生相同的效果,但它会突然导致以下代码被错误编译:
if(foo)
for(...)
{
...
}
else
doSomething();
因为如果你扩展宏,你会得到这个:
if(foo)
if(1)
for(...)
{
...
}
else
doSomething();
else
现在与错误的if
匹配!因此,巧妙地使用if(0) {} else
代替if(1)
可以避免此问题。
最后一点,#define for if(0) {} else for
不会导致无限递归,因为预处理器不会递归替换您当前定义的宏。在这种情况下,它只会做一次更换。
答案 1 :(得分:7)
根据快速搜索,MSVC中的一个错误得到了克服。
据我所知,
for(int i=0...){.....} //later at the same scope level in the same function for(int i=0...){...}
将导致重新定义“i”错误。
如果for语句包含在if语句中,编译器就会按原样运行,这样就不会出现重定义错误(显然它会解释'if'但不是'for'的范围级别)
答案 2 :(得分:2)
因为默认情况下,msvc编译器错误地处理了for语句中声明的变量范围。为了避免这种情况,你必须关闭微软扩展,然后使ms标头不能编译。
我使用(是的,我仍然使用vs6)一个不同的,它不会在vs6中引起警告,尽管英特尔编译器仍然会发现它。
#define for switch(0) case 0: default: for
我不记得我从哪里得到它,但我怀疑我发明了它; - )
我知道其他答案已经说明了大部分内容,但弹出窗口说要确保你回答这个问题。
答案 3 :(得分:1)
已经描述了效果。
拥有它的原因是将C ++代码移植到MSVC。或者,如果您希望C ++代码能够独立运行,那么它也非常有用。例如,您在Linux / MacOSX上开发它,现在想在MSVC中编译它。
它对C ++本身也非常有用。例如:
for(std::set<Foo>::iterator i = myset.begin(); i != myset.end(); ++i) {
// ...
}
for(int i = 0; i < N; ++i) {
// ...
}
我已经看过MSVC代码,通过以下方式解决了这个问题:
for(std::set<Foo>::iterator i1 = myset.begin(); i1 != myset.end(); ++i1) {
// ...
}
for(int i2 = 0; i2 < N; ++i2) {
// ...
}
或者:
{for(std::set<Foo>::iterator i = myset.begin(); i != myset.end(); ++i) {
// ...
}}
{for(int i = 0; i < N; ++i) {
// ...
}}
在这两种情况下(imo)都没那么好。而这个#define是一个小型的黑客,可以使MSVC表现得更加标准。