Каково возможное использование для параметра #define для if (false) {} else for??
В другом вопросе я просто заметил эту маленькую жемчужину мудрости 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++) {...}
Я просто не понимаю, почему разработчики будут использовать этот маленький фрагмент. У кого-нибудь есть идея?
Ответы
Ответ 1
Это исправить ошибку в старых версиях Visual С++ (v6.0 и ранее). В прошлом Visual С++ нарушил правила области видимости переменных, объявленных внутри операторов for
:
// This compiles in old versions of Visual C++, but it is in fact INVALID C++
for(int i = 0; ...)
{
...
}
for(i = 0; ...)
{
}
Другими словами, Visual С++ предоставляет i
область видимости, как если бы она была объявлена вне цикла, и позволяет продолжить ее использование после завершения цикла. Это приводит к созданию кода, такого как приведенный выше фрагмент. В более компиляторах, совместимых с стандартами, i
больше не входит в область определения второго цикла for
, поэтому компилятор выдает ошибку о i
как undefined.
Чтобы исправить это, некоторые люди использовали этот макрос (или очень похожие, эквивалентные макросы):
#define for if(0) {} else for
Это изменяет цикл for
на это:
if(0)
{
}
else
for(int i = 0; ...)
{
...
}
Это ставит цикл for
в дополнительный уровень видимости, так что любые переменные, объявленные в цикле for
, впоследствии будут вне области видимости, независимо от ошибки Visual С++. Это гарантирует, что один и тот же код правильно компилируется как в Visual С++, так и в стандартах, совместимых с компиляторами, и что неправильный код не компилируется правильно.
Также обратите внимание, что если макрос был определен следующим образом:
// 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
не вызывает бесконечную рекурсию, поскольку препроцессор не будет рекурсивно заменять макрос, который вы в настоящее время определяете. В этом случае он будет выполнять только одну замену.
Ответ 2
Согласно быстрому поиску, это ошибка в MSVC, которая преодолевается.
Как я понимаю,
for(int i=0...){.....}
//later at the same scope level in the same function
for(int i=0...){...}
приведет к переопределению ошибки "i".
Если оператор for заключен в оператор if, компилятор работает так, как он должен, чтобы не было ошибки переопределения (по-видимому, он интерпретирует уровни объема "if", но не "for" )
Ответ 3
Поскольку компилятор msvc неправильно обрабатывает область переменных, объявленных в инструкции for по умолчанию. Чтобы избежать такого поведения, вам пришлось отключить расширения Microsoft, которые затем не скомпилировали заголовки ms.
Я использую (да, я все еще использую vs6) другой, который не вызывает предупреждение в vs6, хотя компилятор Intel все еще замечает его.
#define for switch(0) case 0: default: for
Я не могу вспомнить, откуда я его получил, но я сомневаюсь, что я его придумал; -)
Я знаю, что другие ответы уже говорят об этом, но всплывающее сообщение говорит, чтобы вы ответили на вопрос.
Ответ 4
Эффект уже описан.
Поводом для этого является перенос кода на С++ в MSVC. Или это также очень полезно, если вы хотите, чтобы ваша платформа кода на С++ не зависела. Например, вы разработали его на Linux/MacOSX и теперь хотите скомпилировать его в MSVC.
И это также очень полезно для самого С++. Например:
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 более стандартным.