Каково возможное использование для параметра #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 более стандартным.