Как можно использовать «#define for if (false) {} else for»? - PullRequest
45 голосов
/ 12 июня 2009

В другом вопросе я только что заметил эту маленькую жемчужину 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++) {...}

Я просто не понимаю, почему разработчики использовали этот маленький фрагмент. У кого-нибудь есть идея?

Ответы [ 4 ]

92 голосов
/ 12 июня 2009

Это исправление ошибки в старых версиях 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 в дополнительный уровень области видимости, так что любые переменные, объявленные в цикле for, впоследствии будут вне области видимости, независимо от ошибки Visual C ++. Это гарантирует, что один и тот же код корректно компилируется корректно как в 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 не вызывает бесконечной рекурсии, потому что препроцессор не будет рекурсивно заменять макрос, который вы в настоящее время определяете. Это сделает только одну замену в этом случае.

7 голосов
/ 12 июня 2009

Согласно быстрому поиску, это ошибка в MSVC, которая преодолевается.

Как я понимаю,

for(int i=0...){.....} 
//later at the same scope level in the same function
for(int i=0...){...}

приведет к переопределению ошибки 'i'.

Если оператор for заключен в оператор if, компилятор работает так, как должен, чтобы не было ошибки переопределения (очевидно, он интерпретирует уровни области действия «если», но не «для»)

2 голосов
/ 15 декабря 2011

Поскольку компилятор msvc неправильно обрабатывает область переменных, объявленных в операторе for по умолчанию. Чтобы избежать такого поведения, вам пришлось отключить расширения Microsoft, что привело к тому, что заголовки ms не компилировались.

Я использую (да, я все еще использую vs6) другой, который не вызывает предупреждение в vs6, хотя компилятор Intel все еще замечает его.

#define for switch(0) case 0: default: for

Не могу вспомнить, откуда я это взял, но сомневаюсь, что изобрел это; -)

Я знаю, что другие ответы уже говорят о большей части этого, но всплывающее окно говорит, что вы должны ответить на вопрос.

1 голос
/ 26 октября 2009

Эффект уже описан.

Причиной этого является перенос кода 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) {
    // ...
}}

В обоих случаях (имо) не все так хорошо. И этот #define - небольшой взлом, чтобы MSVC вел себя более стандартно.

...