取消定义(#undef)关键字是否合法?

3

我期望以下程序在GCC或Clang中会发出诊断信息:

#undef protected
#undef private

由于[macro.names] / 2:
翻译单元不得定义或取消定义与关键字、表3中列出的标识符或7.6中描述的属性令牌在词汇上完全相同的名称。
“protected”和“private”都列在表3中。通过对N3337进行“无需诊断”的快速查找,未发现任何内容。因此,编译器在这种情况下是否需要报错?

这个子句是否属于17.6.4.3/2中的“行为未定义”范畴? - T.C.
无论如何也没有任何意义,关键词并非由预处理器定义。 - user207421
1
另一方面,有时可能是必要的。我曾经遇到过一些淘气的代码,它说“#define private public”,所以“#undef private”是摆脱这种未定义行为的唯一方法。 - juanchopanza
1
@juanchopanza真的可以“摆脱”UB吗?我认为一旦代码中存在UB,整个程序的行为就是未定义的。因此,#undef private可能不会产生预期的效果。这不是这种情况吗? - The Paramagnetic Croissant
@TheParamagneticCroissant 如果程序使用标准库(或者翻译单元包含任何标准头文件——我不确定哪个),那么它就是未定义行为。否则,它是明确定义的。至于@juanchopanza的建议,他提出了一个实用的建议;代码可能已经包含了未定义的行为。(例如,#define private public 是违反一个定义规则的好方法。) - James Kanze
@T.C. 这就是我引用的那一部分。更普遍地说:库中的约束违规几乎总是未定义行为,因为编译器不需要了解它们(这是库的约束,而不是程序的约束)。 - James Kanze
3个回答

7

[macro.names]是[reserved.names]的一部分,其中规定:“如果程序在保留上下文中声明或定义名称,除非显式允许,否则其行为是未定义的。”因此它的行为是未定义的。

实际上,大多数编译器不会抱怨有两个原因:第一个是因为预处理通常在编译器评估符号是否为关键字之前进行;这是翻译的早期阶段。而且因为只有在你实际使用标准库时,这样的声明才是非法的(尽管其他库,如Posix或Windows,可能并且可能实施类似的规则)。

编辑:

仅作一般性评论:虽然标准中没有全局说明,但是存在一个一般的基本原则,即违反库约束是未定义的行为;意图是编译器不需要了解库,并且实现可以像#include "MyHeader.hpp"一样精确地对待#include <vector>(除了它可能寻找文件的位置)。原始帖子中引用的限制是针对使用库的程序的约束,并且仅适用于这些程序。类似于:

#define while if
int
main( int argc, char** argv )
{
    int retval = 0;
    while ( argc > 0 ) {
        ++ retval;
        -- argc;
    }
    return retval;
}

这是一个完全合法的 C++(和 C)程序,保证返回 1。(当然,我不建议这样做。)


[macro.names]与所有其他子节不同,它没有说“保留”,而是说“不得”。 - Sebastian Redl
@SebastianRedl 或许吧,但是 [reserved.names] 明显适用于 [macro.names]。 - James Kanze
@SebastianRedl 不同措辞的原因可能是因为关键字并不是一般意义上的保留名称;它们是完全独立的实体。 - James Kanze

0

是的,但实际上大多数人并没有。Clang 最近才得到一个警告。


如果“是”是对“编译器是否需要报错”这个问题的回答,那么这是不正确的。 - The Paramagnetic Croissant
Clang 最近收到了一个警告。参考资料? - hlscalon
@TheParamagneticCroissant 我不同意,但会在James Kanze的回答中详细阐述。 - Sebastian Redl

0

使用保留关键字的名称肯定不是一个好主意。然而,大多数编译器不会对此发出警告。

您可以查看C / C++中的关键字重新定义

这是类似的情况。


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