Имеет ли значение порядок, когда #defines используют другие #defines?

В соответствии с ответом на этот вопрос, следующий код является законным:

#define three 3
#define nine three*3

int main()
{
    std::cout << nine;
    return 0;
}

И конечно, он компилируется и работает нормально. Однако в ответе на упомянутый вопрос также говорится, что нужно соблюдать порядок таких директив #define, и тот, который будет использоваться в других #define, должен быть определен перед ними. Но следующий код:

#define nine three*3
#define three 3

int main()
{
    std::cout << nine;
    return 0;
}

Также компилируется и работает отлично, и он печатает "9".

Является ли мой компилятор меня легко отключить, или порядок действительно не имеет значения С#defines, которые используют другие #define s? Не будет ли компиляция неудачной в более сложном проекте?

Следует упомянуть то, что упомянутый вопрос говорит о C, а мой код - в С++. Это где (предполагаемые) различия в поведении происходят?

Ответы

Ответ 1

three макрос должен быть определен только перед использованием макроса nine. Вы можете даже изменить three перед каждым использованием nine:

#define nine three*3
#define three 3

int main()
{
    std::cout << nine; //9
#undef three
#define three 4
    std::cout << nine; //12
#undef three
    //no `three` macro defined here
    int three = 2;
    std::cout << nine; //three * 3 == 6
    return 0;
}

Ответ 2

Препроцессор выполняет несколько прогонов и заканчивается только тогда, когда не найдено других вхождений всех определений. Таким образом, ваши образцы кода работают, но препроцессору требуется еще один запуск в случае второго. Вы должны быть осторожны с рекурсивными определениями. Тогда ppc никогда не выйдет.

Ответ 3

Эти шаги будут выполняться на этапе препроцессора, и все литералы будут заменены их значением. Это можно проверить, если мы скомпилируем этот параметр:

$g++ -save-temps basic.cpp -o out

Это результат после этапа препроцессора (файл basic.ii):

int main()
{
    std::cout << 3*3;
    return 0;
}

для вашей примерной программы. Таким образом, порядок не должен иметь значения, поскольку он является находкой и заменой, и это делается до фактической компиляции программы.

Ответ 4

На самом деле это из-за двухэтапного парсера. На первом шаге он пытается разрешить весь символ, а на втором шаге - фактическое значение.