Странный результат от взаимной ссылки в макросе С++

Рассмотрим следующий код С++:

#include <bits/stdc++.h>
using namespace std;

string TRUE = "true";
string FALSE = "false";

#define TRUE FALSE
#define FALSE TRUE

int main()
{
    cout << TRUE << endl;
    cout << FALSE << endl;
}

Скомпилированный с использованием GCC 4.9.2, приведенные выше выходы кода:

true
false

Какова логика выпускаемой продукции? Я ожидал "false\ntrue\n" или даже "false\nfalse\n", но я не могу найти аргументы за этот фактический вывод.

Как работает пара #defines в этом случае?

Ответы

Ответ 1

Это описано в [cpp.rescan]/1-2:

16.3.4 Повторное сканирование и дальнейшая замена

  • После замены всех параметров в списке замещения и обработки # и ## все маркеры предварительной обработки маркеров помещаются. Затем результирующая последовательность токенов предварительной обработки повторно сканируется вместе со всеми последующими токенами предварительной обработки исходного файла для замены других имен макросов.
  • Если имя заменяемого макроса найдено во время этого сканирования списка замены (не считая остальных токенов предварительной обработки исходного файла), он не заменяется. Кроме того, если какие-либо вложенные замены встречаются с именем заменяемого макроса, он не заменяется. Эти неперемещаемые маркеры предварительной обработки макросов больше не доступны для дальнейшей замены, даже если они позже (повторно) рассмотрены в контексты, в которых этот токен предварительной обработки имени макроса в противном случае был бы заменен.

(Акцент мой)

Итак: когда TRUE встречается в коде, он заменяется на FALSE. Последовательность сканируется, FALSE заменяется на TRUE. Последовательность повторно сканируется, TRUE найден, но больше не имеет права на замену, и поэтому он остается. То же самое относится к расширению FALSE (с замененными идентификаторами).

Ответ 2

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

Input:

cout << TRUE << endl;

TRUE - макрос, он расширен:

cout << FALSE << endl;

Расширенный текст, повторно просматриваемый для макросов, FALSE найден и расширен:

cout << TRUE << endl;

Расширенный текст повторно сканируется для макросов, TRUE найден; но он уже был расширен один раз, поэтому ничего не происходит, и он остается как TRUE для последующей компиляции (который затем находит глобальную переменную).