为什么 ## 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 中没有任何意义(注释除外)。
为什么会出现以下错误?
#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 中没有任何意义(注释除外)。