Подтвердить что ты не робот

Два соседних оператора ##

Может кто-нибудь объяснить мне, почему два оператора конкатенации не выдают какой-либо ошибки препроцессором?:

#define Z(x) x ## ## 3
Z(3)

приводит к:

33

Стандарты говорят, что:

... каждый экземпляр маркера предварительной обработки ## в списке заметок (не из аргумента) удаляется, а предыдущая предварительная обработка токен объединен с следующим токеном предварительной обработки

поэтому я ожидал бы, что preproceror сначала попытается выполнить x со вторым ##, который кажется странным. Это не дает никакого действительного токена, поэтому я бы ожидал хотя бы предупреждения. Ни gcc, ни VC не выдают никаких предупреждений.

Я был бы признателен за некоторое объяснение того, как это работает и почему. В стандарте упоминаются временные токены placemaker, которые объяснят, почему это работает, но должен быть один такой токен между двумя "двойными острыми". Проблема в том, что токены placemaker генерируются, когда параметр не содержит токенов, и между обоими операторами concat нет параметра.

4b9b3361

Ответ 1

(Стандарты C и С++ имеют по существу идентичную формулировку в разделе 6.10.3.3 и разделе 16.3.3 соответственно. В случае незначительных различий я взял кавычки из C11.)

Порядок, в котором обрабатываются операторы ##, явно не определен: ( "Порядок оценки операторов ## не указан", последнее предложение пункта 3, а также см. параграф 2 предыдущего раздела). Поэтому вы не можете сказать, что "preproceror сначала пытается выполнить concat x со вторым ##"; сначала можно попытаться выполнить первый ## с помощью 3. Это тоже не привело бы к действительному токену, так что это немного каламбур. Но важно помнить.

Вопрос заключается в том, является ли утверждение о том, что неуказанный порядок оценки позволяет чередовать оценку. Другими словами, может ли препроцессор соответствовать стандарту, сначала удалив второй ##, затем удалив первый и, наконец, создав одну конкатенацию? Разумеется, в модели исполнения ясно, что операции с нечеловеческими данными допускают чередование. (См. Примечание 13 в разделе 5.1.2.3. В С++ слова "могут перекрываться"; & section; 1.9/13)

Это может быть немного растянуто, но также стоит отметить, что после конкатенации:

Если результат не является допустимым токеном предварительной обработки, поведение undefined.

Это не помечено как ограничение, поэтому сообщение об ошибке не требуется. И поскольку поведение undefined избавляет компилятор от каких-либо обязательств перед стандартом, я полагаю, что gcc полностью входит в его права производить наблюдаемое поведение.

Короче говоря, строка замены макроса, содержащаяся в исходном вопросе, либо включает неопределенное, либо undefined поведение, но не нарушение ограничения. Следовательно, компилятор не обязан производить диагностику.

Не производить диагностику в этом случае можно рассматривать как проблему качества реализации. С другой стороны, я не знаю ни одного компилятора, который выдает предупреждение для макросов с неоднозначным порядком оценки ##. Помимо ограничения того, что список макроопределений не может начинаться или заканчиваться маркером ##, который должен быть диагностирован компилятором, полностью ответственен программист, чтобы гарантировать, что выражения конкатенации четко определены.