考虑这段代码:
#define FOO A::
struct A
{
int x;
};
int FOO *ptr = &A::x;
Clang(18.1.0,无标志)发出警告:
<source>:8:5: warning: '::' and '*' tokens forming pointer to member type appear in different macro expansion contexts [-Wcompound-token-split-by-macro]
MSVC 和 GCC 按原样接受代码。
我以为它希望我将
::
和 *
粘贴在一起,但这会在所有三个编译器中触发硬错误:
#define CAT(x, y) CAT_(x, y)
#define CAT_(x, y) x##y
int CAT(FOO,*) ptr = &A::x;
error: pasting formed '::*', an invalid preprocessing token
根据标准,第一个片段是否合法?如果是,除了
#pragma
之外,还有什么方法可以消除此警告吗?
::*
不是单个 pp-token。 pp-token 的运算符在 [lex.operators]/1 中给出。在那里,::
和*
是两个单独的标记,并且没有::*
的条目。因此,尝试连接两个 pp-token 来给出 ::*
是非法的,因为令牌粘贴运算符必须生成单个 pp-token 作为其结果 ([cpp.concat]/3)。
当然允许两个单独的令牌来自两个不同的宏扩展;这和类似的东西没有什么不同
#define foo const
#define bar int
foo bar x; // const int x;
const
和 int
是两个独立的标记,当它们在此上下文中彼此相邻出现时,暗示类型 const int
。同样,当在某些上下文中 ::
标记后跟 *
标记时,您可以声明一个指向成员的指针或命名一个指向成员的指针类型。
确实,
::
和*
可以用空格分隔而不改变它们的含义。预处理完成后,语言中任何不同的标记都会出现这种情况;请参阅 [lex.phases]/1.7。
C++ 标准中没有“复合令牌”这样的东西。 Clang 开发人员弥补了这一点。您可以尝试在 https://eel.is/c++draft/full 中搜索“复合令牌”,但您将找不到任何内容。当
::
和 *
来自不同的宏扩展 或由空格 分隔时,Clang 开发人员似乎感到困惑并且可能是无意的。事实上,我确信这会让某些人感到困惑,但我确信这样做也是有正当理由的,所以我建议禁用此警告。