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

Why does the ## aka token-pasting operator not work for comments in C and C++?

为什么会出现以下错误?

#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(/, /) 会告诉预处理器用 // 替换它,然后当它进一步解析时,整行将被解释为注释。

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

对于 C++:

## 必须生成有效的预处理令牌。

// 不被视为预处理标记。相反,包括 // 介绍者在内的评论被认为是 白色 space,请参阅 C++17 标准(最终草案)的 [lex.token]/1

如果##没有生成有效的预处理令牌,例如这里,程序有未定义的行为。参见 [cpp.concat]/3。这意味着编译器甚至不需要通过错误消息来告知您搞砸了。

在执行预处理器指令(例如宏定义和替换)之前,所有注释都从源文件中删除,因此即使您可以生成 // 标记,它也不会被替换,而是语法错误。参见 [lex.phases]/1.3-1.4

此答案适用于 C,在 C++ 中类似。

示例与 C11 standard 6.4.9p3:

中的示例完全相同
      #define glue(x,y) x##y
      glue(/,/) k();                     // syntax error, not comment

您看到的错误:

error: pasting "/" and "/" does not give a valid preprocessing token

是因为##的结果需要预处理token。简而言之,preprocessing token are identifiers, preprocessing numbers, string literals, punctuators, and other. (See also gcc docs on tokenization). The resulting // string is not a preprocessing token, so the result of ## here would not be a preprocessing token. The C standard 6.10.3.3p3 表示如果 ## "is not a valid preprocessing token, the behavior is undefined" 的结果。您正在使用的编译器选择在这种情况下发出错误。它不会像以下一样不起作用:

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

也许例如从另一边来看,例如%=是一个valid token, a punctuator。以下都可以:

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

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