На самом деле, это зависит от вашей интерпретации стандарта языка. Например, в mcpp, реализации препроцессора, которая строго соответствует тексту языкового стандарта, вторая также выдает CAT(x, y);
[дополнительные результаты были удалены из результата]:
C:\dev>mcpp -W0 stubby.cpp
#line 1 "C:/dev/stubby.cpp"
CAT(x, y) ;
CAT(x, y) ;
C:\dev>
Существует известное несоответствие в спецификации языка C ++ (такое же несоответствие присутствует в спецификации C, хотя я не знаю, где находится список дефектов для C). В спецификации говорится, что окончательное значение CAT(x, y)
не должно заменяться макросами. Намерение, возможно, состояло в том, что это должно быть заменено макросом.
Цитировать связанный отчет о дефектах:
Еще в 1980-х годах несколько человек из WG14 поняли, что между словами «без замены» и попытками создать псевдокод существует небольшая разница.
Решение комитета состояло в том, что никакие реалистичные программы "в дикой природе" не рискнут проникнуть в эту область, и попытка уменьшить неопределенности не стоит риска изменения статуса соответствия реализаций или программ.
Итак, почему мы получаем другое поведение для M(0)
, чем для N(0)
с наиболее распространенными реализациями препроцессора? При замене M
второй вызов CAT
полностью состоит из токенов, полученных в результате первого вызова CAT
:
M(0)
CAT(M_, 0)
CAT_I(M_, 0)
M_0
CAT(x, y)
Если вместо этого вместо CAT(M, 0)
определено M_0
, замена будет повторяться бесконечно. Спецификация препроцессора явно запрещает эту «строго рекурсивную» замену путем остановки замены макроса, поэтому CAT(x, y)
не заменяется макросом.
Однако при замене N
второй вызов CAT
состоит из только частично токенов, полученных в результате первого вызова CAT
:
N(0)
CAT(N_, 0) ()
CAT_I(N_, 0) ()
N_0 ()
CAT(x, y)
CAT_I(x, y)
xy
Здесь второй вызов CAT
формируется частично из токенов, полученных в результате первого вызова CAT
, и частично из других токенов, а именно ()
из списка замены N
. Замена не является строго рекурсивной, и поэтому при замене второго вызова CAT
она не может привести к бесконечной рекурсии.