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

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

本文关键字:C++ 注释 适用于 不适用 aka 令牌 运算符 为什么      更新时间:2023-10-16

为什么会出现以下错误?

#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的,在C++中是相似的。

该示例与 C11 标准 6.4.9p3 中的示例完全相同:

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

您看到的错误:

错误:粘贴"/"和"/"未提供有效的预处理令牌

由此产生的##结果需要预处理令牌。简而言之,预处理标记是标识符、预处理数字、字符串文本、标点符号等。(另请参阅关于标记化的 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.

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

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

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

对于C++:

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

//不被视为预处理令牌。相反,注释,包括//介绍人,被认为是空白的,参见C++17标准(最终草案(的[lex.token]/1。

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

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