构成成员指针类型的`::`和`*`可以来自不同的宏扩展,还是必须以单个标记的形式出现?

问题描述 投票:0回答:1

考虑这段代码:

#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
之外,还有什么方法可以消除此警告吗?

c++ language-lawyer c-preprocessor
1个回答
0
投票

::*
不是单个 pp-tokenpp-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 开发人员似乎感到困惑并且可能是无意的。事实上,我确信这会让某些人感到困惑,但我确信这样做也是有正当理由的,所以我建议禁用此警告。

© www.soinside.com 2019 - 2024. All rights reserved.