Каково возможное использование #define для if (false) {} else для?

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

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


person paxdiablo    schedule 12.06.2009    source источник


Ответы (4)


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

person Adam Rosenfield    schedule 12.06.2009

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

Я так понимаю,

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

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

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

person Earlz    schedule 12.06.2009
comment
И в чем актуальность? Я не понимаю. Более того, я бы не сказал, что это баг. Разве не так работают прицелы? - person Dunya Degirmenci; 12.06.2009
comment
да, это звучит более реалистично. В. Почему они поставили условие if (false) {} вместо простого if(1). - person Ryan Oberoi; 12.06.2009
comment
MSVC 6.0 предшествовал стандарту C99, насколько мне известно... VC 6 предшествовал некоторым стандартным стандартам. У него есть «расширение», которое заключается в том, что после определения «i» оно существует до конца функции. - person Earlz; 12.06.2009
comment
Если поставить только if(1) for, это будет ошибкой. Предположим, у вас был код if(condition) for(...) { }; else do_happy_thing(); — теперь ваш код неисправен, и вам будет довольно сложно понять, почему. - person yfeldblum; 12.06.2009

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

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

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

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

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

person Richard Broadhurst    schedule 14.12.2011

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

Причина в том, что он должен портировать код 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) {
    // ...
}

Or:

{for(std::set<Foo>::iterator i = myset.begin(); i != myset.end(); ++i) {
    // ...
}}

{for(int i = 0; i < N; ++i) {
    // ...
}}

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

person Albert    schedule 26.10.2009