我一直在针对一些非常“非默认”的警告编译我的 C 代码,并发现了一个潜在的 GCC 错误 - 由
-Wc++-compat
引起的误报。 在解释任何内容之前,先介绍一下代码可能会更容易:
main.c
#include <stdio.h>
#define X_KEYWORDS \
X(bool) \
X(not) \
X(and) \
X(or) \
X(xor) \
X(nullptr) \
X(true) \
X(false)
int main(void)
{
# define X(keyword) puts(#keyword);
X_KEYWORDS
# undef X
return 0;
}
当使用
-Wc++-compat
编译时,GCC 会打印以下诊断信息:
$ gcc main.c -Wc++-compat
main.c:5:11: warning: identifier "not" is a special operator name in C++ [-Wc++-compat]
5 | X(not) \
| ^
main.c:6:11: warning: identifier "and" is a special operator name in C++ [-Wc++-compat]
6 | X(and) \
| ^
main.c:7:11: warning: identifier "or" is a special operator name in C++ [-Wc++-compat]
7 | X(or) \
| ^
main.c:8:11: warning: identifier "xor" is a special operator name in C++ [-Wc++-compat]
8 | X(xor) \
|
如您所见,它警告我有关宏定义内部的构造;这些还没有在任何地方扩展。
main
的主体在这里无关紧要;它甚至可以是空的 - int main(void) {}
- 并且无论如何都会出现相同的警告。我只是想证明这个特定的 X-Macro 有一个有效的用例。
这些警告永远不会出现在实际的扩展代码中。我的代码片段将它们扩展为对我来说有效的字符串文字。我不明白它如何违反 C++ 方言。
那些“错误”的标识符与 libc 中定义的标识符相同
<iso646.h>
(?)。
请注意,我还在宏体内使用了其他特定于 C++ 的关键字,例如 bool
或 nullptr
,并且警告并未指向它们。
无论如何,上述行为似乎不会发生在 clang 中:
$ clang main.c -Wc++-compat
并且可以像 C++ 一样使用我目前可用的所有编译器进行编译:
$ mv main.c main.cpp
$ g++ main.cpp
$ cl main.cpp /nologo
main.cpp
$ clang++ main.cpp
<iso646.h>
的内容?(参考资料)
$ uname -a
Linux MAL200424 6.11.6-arch1-1 #1 SMP PREEMPT_DYNAMIC Fri, 01 Nov 2024 03:30:41 +0000 x86_64 GNU/Linux
$ gcc --version
gcc (GCC) 14.2.1 20240910
$ clang --version
clang version 18.1.8
godbolt 链接:https://godbolt.org/z/8jo363EbP
GCC 手册:https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/Warning-Options.html#index-Wc_002b_002b-compat
关于
- 我可以安全地假设这是一个 GCC 特定的错误吗?
正如您自己观察的那样,诊断标记了宏
X_KEYWORDS
的定义,而不是它扩展的位置。 此时,编译器不知道如何使用该宏,因此最好将该行为描述为 GCC 非常谨慎,而不是有错误。 我认为它是在告诉您,可能有一些方法可以使用与 C++ 不兼容的宏。 如果您确实以这种方式使用它,我预计您会得到额外的诊断。
所以不,你不应该认为诊断有问题,但你也不应该认为它们表明你的整个程序不是有效的 C++。
- 或者在任何情况下这些诊断都是有效的吗?
见上文。
- 为什么它只警告来自
的内容?
在考虑的特定预处理标记中,GCC 警告的标记在 C++ 中属于
preprocessing-op-or-punc
类型,而在 C 中属于 identifier
类型。其他标记在两种语言中都属于 identifier
类型。 这似乎就是 GCC 将前一组中的人员称为“特殊操作员名称”的意思。 这也是潜在不兼容的基础。