#ifdef指令末尾有额外的标记

6
为什么以下代码可以编译通过?
#ifdef C++11
// ...
#endif

int main() {}

gcc 4.8.0 给了我以下警告:

#ifdef 指令中存在额外的标记

根据标准,宏名称只能包含字母、数字和下划线字符。

可能是因为这个原因吗?

ISO/IEC 14882:2011

16.1 条件包含 [cpp.cond]

6 每个指令的条件按顺序检查。如果它计算结果为false(零),则跳过它所控制的组:指令仅通过确定指令的名称进行处理,以便跟踪嵌套条件的级别;组中的其余预处理标记将被忽略,以及组中的其他预处理标记。只有第一个控制条件计算结果为true(非零)的组会被处理。如果没有任何条件计算结果为true,并且存在 #else 指令,则将处理由 #else 控制的组;如果缺少 #else 指令,则跳过所有组,直到 #endif。151

我无法正确理解此引用。


我相信预处理器标识符遵循与变量标识符相同的规则,尽管首选大写。 C ++ 11不起作用。 这个SO问题应该会帮助你:https://dev59.com/Nmgv5IYBdhLWcg3wSe0f - Eric Jablow
@Eric Jablow 我知道__cplusplus宏,我想知道为什么这段代码可以在gcc 4.8.0、clang 3.2、icc 13.0.1和MSVC-11中编译。 - FrozenHeart
1
抱歉,我应该仔细阅读。也许GCC太宽容了。 - Eric Jablow
2个回答

4
就C++而言,#ifdef C++11是语法错误。并没有规定编译器必须拒绝具有语法错误的程序。

1.4 实现兼容性 [intro.compliance]

可诊断规则集包含国际标准中的所有语法和语义规则,除了那些明确注明“不需要诊断”的规则或被描述为导致“未定义行为”的规则。

[...]

如果一个程序包含任何可诊断规则的违规或实现不支持该构造的条款的出现,则符合要求的实现将发出至少一个诊断消息。

警告是一种诊断消息。只要编译器确保显示一个诊断消息,他们就可以完全按照自己的权利继续成功编译程序。由于编译器历史上接受这样的指令,并且接受这样的指令不与标准的要求冲突,所以他们继续这样做。
至少在GCC方面,您可以使用-pedantic-errors选项要求将所有标准所需的诊断信息设置为严格的错误。
$ printf "#ifdef C++11\n#endif\n" | gcc -std=c++11 -pedantic-errors -E -x c++ -
# 1 "<stdin>"
# 1 "<command-line>"
# 1 "<stdin>"
<stdin>:1:9: error: extra tokens at end of #ifdef directive

1

一个#ifdef的定义如下所示(摘自§16.1)

# ifdef      标识符 换行符

用类似正则表达式的符号表示,标识符为:[a-zA-Z_][a-zA-Z_0-9]* (*)

关键是:你声明的宏并非C++11。实际上它是C(请参见this live example)。预处理器会忽略++11部分。标识符(即C)后唯一允许的字符是换行符,但正如hvd的答案中所说,从 §1.4开始,语法错误只会强制生成诊断信息,这里是警告;我认为之所以采用这种方式而不是错误提示,是为了与旧代码兼容,以前可能使用了这样的名称。

另外:引文解释了#ifdef / #elif / #else / #endif如何协同工作,而不是指定条件的方式。

我没有标准的副本。我在回答中使用了n3485的草案。
(*) 标识符中可以有实现定义字符,但这不影响您的问题。请注意,变量、类名、宏等都遵循相同的标识符规则。

是的,但这并不意味着这是标准。gcc、clang并不是标准,它们试图遵循它。如果它们符合标准(在这一点上),它们会输出一个错误,而不是一个警告(除非我漏掉了什么)。 - Synxis
1
是的,当看到“实现定义”的部分时,我也这么想。然而,标识符不是C++11,而是C,这意味着+不是*identifier-nondigit*的一部分,这就是为什么我认为这不是你的代码被接受的原因。 - Synxis
“其他实现定义的字符”例如$。绝对不是+,因为这会导致有效代码出现问题,例如int a = 1; int b = 2; return a+b; - user743382
1
@KeithThompson 即使这样也是无效的:“在对象宏的定义中,标识符和替换列表之间应该有空格。” - user743382
@KeithThompson 这是为了更好地“演示”定义的宏不是C++11。我知道这是语法错误。Gcc只会输出一个警告并继续解析代码。原因在hvd的回答中有解释。 - Synxis
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接