Почему оператор ## aka для вставки токенов не работает для комментариев в C и C ++? - PullRequest
1 голос
/ 12 января 2020

Почему возникает следующая ошибка?

#include <iostream>

#define concatenate(t1, t2) t1 ## t2 // Concatenate token1 and token2

int main()
{
    int myVar = 22;
    std::cout << concatenate(my, Var); // Compiles fine and outputs the value of myVar

    concatenate(/, /) So I thought that this would be a comment but this is a compile-time error
    // error: pasting "/" and "/" does not give a valid preprocessing token

    return 0;
}

Я думал, что concatenate(/, /) скажет препроцессору заменить его на //, а затем при дальнейшем разборе будет интерпретирована вся строка в качестве комментария.

Что на самом деле происходит в этом случае?

Ответы [ 2 ]

4 голосов
/ 12 января 2020

Этот ответ для C, он аналогичен в C ++.

Пример буквально такой же, как в C11 стандартном 6.4.9p3 :

      #define glue(x,y) x##y
      glue(/,/) k();                     // syntax error, not comment

Ошибка, которую вы видите:

ошибка: вставка "/" и "/" не дает действительный токен предварительной обработки

происходит из-за результата ## требуется предварительная обработка токенов. Короче говоря, токен предварительной обработки - это идентификаторы, числа предварительной обработки, строковые литералы, знаки препинания и другие. (См. Также g cc документы по токенизации ). Результирующая строка // не является токеном предварительной обработки, поэтому результат ## здесь не будет токеном предварительной обработки. Стандарт C 6.10.3.3p3 гласит, что если результат ## "не является допустимым токеном предварительной обработки, поведение не определено". Компилятор, который вы используете, решает выдать ошибку в таком случае. Он не будет работать так же, как и следующие:

concatenate(:, b) // error, `:b` is not a preprocessing token
concatenate(%, b) // error, `%b` is not a preprocessing token
// etc.

Может быть, например, с другой стороны, например %= - это действительный токен, пунктуатор . Все в порядке:

concatenate(%, =)
concatenate(/, =)
concatenate(a /, = b)
concatenate(<, :)

В любом случае, даже если // будет допустимой предварительной обработкой, комментарии заменяются одним пробелом в фазе перевода 3, в то время как препроцессор выполняется в фазе перевода 4 после комментариев удалены, см. C11 фазы перевода . Таким образом, даже если это приведет к // токену, он будет недействительным, поскольку ничего не значит в C (кроме комментариев).

3 голосов
/ 12 января 2020

Для C ++:

## должен приводить к действительному токену предварительной обработки.

// не считается токеном предварительной обработки. Вместо этого комментарии, включая вводчик //, считаются пробелами , см. [lex.token] / 1 стандарта C ++ 17 (окончательный вариант).

Если ## не генерирует действительный токен предварительной обработки, например, здесь, программа имеет неопределенное поведение. См. [cpp .concat] / 3 . Это означает, что компилятору даже не требуется сообщать с сообщением об ошибке, что вы напутали.

Все комментарии удаляются из исходного файла до выполнения директив препроцессора, таких как определение и замена макросов, поэтому даже если бы вы могли сгенерировать токен //, он не был бы заменен и был бы синтаксической ошибкой. См. [lex.phases] /1.3-1.4

...