为什么## aka 标记粘贴运算符不适用于 C 和 C++ 中的注释?

Ard*_*der 3 c c++ concatenation token c-preprocessor

为什么会出现以下错误?

#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;
}
Run Code Online (Sandbox Code Playgroud)

我认为这concatenate(/, /)会告诉预处理器用 a 替换它,//然后当它进一步解析时,整行将被解释为注释。

在这种情况下实际发生了什么?

Kam*_*Cuk 7

这个答案是针对 C 的,它在 C++ 中是类似的。

该示例实际上与C11 标准 6.4.9p3 中的相同:

      #define glue(x,y) x##y
      glue(/,/) k();                     // syntax error, not comment
Run Code Online (Sandbox Code Playgroud)

您看到的错误:

错误:粘贴“/”和“/”没有给出有效的预处理标记

来自##需要预处理令牌的结果。简而言之,预处理标记是标识符、预处理数字、字符串文字、标点符号等。(另请参阅关于标记化的 gcc 文档)。结果//字符串不是预处理标记,因此##此处的结果不会是预处理标记。在C标准6.10.3.3p3规定,如果结果##“不是一个有效的预处理标记,这种行为是未定义”。在这种情况下,您使用的编译器选择发出错误。它不会以与以下不起作用的方式相同的方式工作:

concatenate(:, b) // error, `:b` is not a preprocessing token
concatenate(%, b) // error, `%b` is not a preprocessing token
// etc.
Run Code Online (Sandbox Code Playgroud)

也许例如从另一边,例如%=是一个有效的标记,一个标点符号。以下是可以的:

concatenate(%, =)
concatenate(/, =)
concatenate(a /, = b)
concatenate(<, :)
Run Code Online (Sandbox Code Playgroud)

无论如何,即使//是有效的预处理,注释也会在翻译阶段 3 中替换单个空格,而在删除注释后,在翻译阶段 4 中执行预处理器,请参阅C11 翻译阶段。因此,即使它会导致//令牌,它也是无效的,因为它在 C 中没有任何意义(注释除外)。