在这篇来自本周古鲁的文章中,有人说:It is illegal to #define a reserved word.这是真的吗?我在标准中找不到任何东西,例如,我已经看到程序员重新定义了新的。
- 使用#define来改变保留词的含义是完全可能的。事实上,它经常被用于国际上模糊的C代码竞赛条目中。这是可能的,因为在实际的C编译器运行之前,由#define定义的宏被一个单独的程序替换。
- 确定他们没有让新的操作员超载吗?由于预处理器首先运行,重新定义关键字的问题是,用替换字符串替换关键字,因此不希望这样做的代码很可能在之后被破坏。这样做通常是一个坏主意,你为什么要这样做呢?
- @泰德:其思想是在源文件中重新定义新的关键字,以便调用平台特定的实现:Macintosh上的#define new newMac和PC上的#define new newPc。在其他翻译单元中,相应的函数将定义平台特定的内存分配器。我想这个想法是继续在任何地方使用new,同时在不可能将每个调用重命名为new时有一个特定于平台的行为。
- 回到今天,我们用#define for if (false) else for修复了Visual Studio 6在for循环中声明的变量范围错误。
- 你所说的"我已经见过程序员重新定义新的东西"是什么意思?你看到过使用#define的例子吗?或者您指的是重载new运算符?这是两件截然不同的事情。
- @Monadnewb我已经看过#define new newPS3。电子游戏产业可能有点像动物园。
17.4.3.1.1宏名称[lib.macro.name]
1 Each name defined as a macro in a header is reserved to the implementation for any use if the translation unit includes the header.164)
2 A translation unit that includes a header shall not contain any macros that define names declared or defined in that header. Nor shall such a translation unit define macros for names lexically identical to keywords.
顺便说一下,new是一个运算符,用户可以通过提供自己的版本来超载(替换)。
- 请注意,该规则仅适用于包含标准头的源。如果翻译单元不包含标准标题,则重新定义关键字是完全合法的。(不过,它对代码的可读性没有太大作用。)
- 所以只要我的翻译单元不包含标题,我就可以使用#define new:p?@Jameskanze:为什么是标准标题?
- @标准(模板)库??
- @Victor语言允许编译器知道每个标准头中都有什么,所以它不必每次都重新编译它们。因此,禁止通过重新定义这些标题中的某些单词来更改它们。你如何处理自己的邮件头取决于你自己!
- BoPersson,在这种情况下,它更像是允许在标准标头中使用普通C++代码的标准。你能想象如果你在包括之前做了#define while for会发生什么吗?该标准还要求某些"函数"是宏,因此即使在包含头之后重新定义关键字,也可能会影响到事情。
- @泰德:我只是指这样一个事实,即本文摘录在更一般的意义上讲的是"headers",而不是"standard"headers。我想知道为什么詹姆斯说这只适用于包含标准头的源文件。
- 在C++ 03标准中,术语头是在7.4.1.2页眉[LI.Healths]中定义的:"在标题中声明或定义C++标准库的元素(适当时)。"这与我们通常认为是头文件(.h文件)不符。标准没有概念.cpp文件和.h文件之间的区别;它们都定义为源文件。
- 对于C++ 11,定义是类似的:"在标题中声明或定义C++标准库的每个元素(适当的话)。"这次很明显,每个元素可以在不同的标题中。但是,[macro.name]使用术语"标准库头"来清除。
- @哦,好的。谢谢你把事情弄清楚:)
C++ 11对应的部分:
17.6.4.3.1宏名称[宏名称]
1 A translation unit that includes a standard library header shall not #define or #undef names declared in any standard library header.
2 A translation unit shall not #define or #undef names lexically identical to keywords.
从C++ 03中删除了第1段。第二段被分成两段。前半部分现在已更改为专门声明它只适用于标准头段。第二点已经扩展到包含任何翻译单元,而不仅仅是包含标题的翻译单元。
但是,本标准本节概述(17.6.4.1[约束条件.概述])规定:
This section describes restrictions on C++ programs that use the facilities of the C++ standard library.
因此,如果你不使用C++标准库,那么你可以做你想做的事情。
因此,在C++ 11的上下文中回答你的问题:如果你使用C++标准库,你不能定义(或不定义)任何翻译单元中与关键字相同的任何名称。
他们实际上是错的,或者至少没有把整个故事讲出来。它被禁止的真正原因是它违反了一个定义规则(顺便提一下,这也是它非法的第二个原因)。
要看到它实际上是被允许的(重新定义关键字),至少如果不使用标准库,您必须查看标准的完全不同的部分,即翻译阶段。它说,输入只在预处理发生之前分解成预处理器令牌,看那些在private和fubar之间没有区别的令牌,它们都是identifiers到预处理器。稍后,当输入分解为token时,替换已经发生。
有人指出,对要使用标准库的程序有限制,但不明显的是,重新定义private的示例是这样做的(与"person 4:语言律师"片段相反,后者使用它来输出到cout)。
上一个例子中提到,技巧不会被其他翻译单位践踏,也不会被其他翻译单位践踏。考虑到这一点,您可能应该考虑到在其他地方使用标准库的可能性,这将使此限制生效。
如果你不想有人使用Goto,你可以做一件事。把下面的代码放到他不会注意到的地方。
1
| #define goto { int x = *(int *)0; } goto |
现在,每当他试图使用goto语句时,他的程序都会崩溃。
- 赞成欢闹
- @是的,但你有没有问过自己是什么让它有趣?答案是人类是虐待狂的生物,这是恐惧的一部分…+一指人性。
- 这是未定义的行为,最好避免。
- 如果你认为goto是坏的,就像那样,你不应该写程序,如果你能说服我一个人绝对不能使用goto,我会非常惊讶。
- @伊哈罗布同意了。有限状态机被认为是goto的潜在良好用途。
- 你关于程序总是崩溃的说法是不对的。因此,真正的程序员不应该这样做。在许多编译器上,语句将被优化,程序不会崩溃。编译器可以轻松地优化block语句中的所有内容,因为x无论如何都是未使用的。即使它没有被优化,它仍然是未定义的行为,而不是保证崩溃。
- 如果您想惩罚使用goto,那么创建不可靠的运行时错误是一种愚蠢的方法。如果必须这样做,请将其设为编译时错误:#define goto @。
- @是的,但是goto不一定是实现有限状态机的最佳方法;循环中的switch语句使当前状态显式化。关于何时使用goto合适的更多讨论,请参阅此答案。
- @艾米:你说得对,他应该写:#define goto { int x = *(int volatile *)0; } goto。但它将扩展为2个语句,并在if (xx) goto yy; else ...中导致语法错误。他因破坏语言而应得到更多的反对票。
据我所知,这并不是非法的-如果你这样做的话,我遇到的任何编译器都不会产生错误。
#defining某些关键字可能由于其他原因在编译时产生错误。但其中很多只会导致非常奇怪的程序行为。
- 这些定义可能会导致标准库出现问题。
- 可能,在某些部分。我的问题是,尽管它是非法的,但似乎没有任何要求编译器发布诊断。根据您使用的标准库的哪一部分以及您在代码中定义的位置,程序可以很好地编译。
- 像很多事情一样,这是未定义的行为。对于编译器来说,检测它并导致编译时错误并不困难,但我不知道有什么困难。
- -1因为这是非法的,参见其他答案。
- 非法但几乎总是被忽视