引用保留名称肯定是非法的吗?
Is it definitely illegal to refer to a reserved name?
在std-proposals列表中,给出了以下代码:
#include <vector>
#include <algorithm>
void foo(const std::vector<int> &v) {
#ifndef _ALGORITHM
std::for_each(v.begin(), v.end(), [](int i){std::cout << i; }
#endif
}
为了这个问题的目的,让我们忽略为什么给出该代码以及为什么这样写(因为有一个很好的理由,但它在这里无关紧要)。它假设 _ALGORITHM
是标准 header <algorithm>
内部的 header 守卫,与某些已知的标准库实现一起提供。这里没有便携性的内在意图。
现在,_ALGORITHM
当然是保留名称,根据:
[C++11: 2.11/3]:
In addition, some identifiers are reserved for use by C++ implementations and standard libraries (17.6.4.3.2) and shall not be used otherwise; no diagnostic is required.
[C++11: 17.6.4.3.2/1]:
Certain sets of names and function signatures are always reserved to the implementation:
- Each name that contains a double underscore
_ _
or begins with an underscore followed by an uppercase letter (2.12) is reserved to the implementation for any use.
- Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.
我一直觉得这段话的目的是防止程序员定义/变异/取消定义 符合上述标准的名称,以便标准库实现者可以使用这些名称而不必担心与客户端代码发生冲突。
但是,在 std-proposals 列表中,有人声称此代码本身就是 ill-formed,仅用于引用此类保留名称。我现在可以看到 [C++11: 2.11/3]:
中的短语“不得以其他方式使用”的使用可能确实暗示了这一点。
例如,给出的一个实际理由是宏 _ALGORITHM
可以扩展为一些擦除硬盘驱动器的代码。但是,考虑到该规则的可能意图,我认为这种可能性更多地与 _ALGORITHM
名称的明显 implementation-defined* 性质有关,而与它的存在关系不大引用它是完全非法的。
* “implementation-defined”是英语语言意义上的,而不是短语的 C++ 标准意义
我想说的是,只要我们对获得 implementation-defined 结果感到高兴,并且我们应该研究该宏对我们的实施意味着什么(如果它存在的话!) , 如果我们不尝试修改它,那么引用这样的宏本质上不应该是非法的。
例如,到处使用如下代码来区分编译为 C 的代码和编译为 C++ 的代码:
#ifdef __cplusplus
extern "C" {
#endif
而且我从来没有听到过对此的抱怨。
那么,你怎么看? “不得以其他方式使用”包括简单地写这样一个名字吗?又或者可能不打算这么严格(这可能指向调整标准措辞的机会)?
它是否合法是特定于实现(和特定于标识符)的。
当标准授予实施使用这些名称的唯一权利时,这包括使这些名称在用户代码中可用的权利。如果一个实现这样做了,那就太好了。
但是,如果实施没有明确赋予您权利,那么从 "shall not be used otherwise" 可以清楚地看出标准没有,并且您有未定义的行为。
重要的部分是"reserved to the implementation"。这意味着编译器供应商可能会使用这些名称甚至记录它们。然后,您的代码可以使用记录的这些名称。这通常用于像 __builtin_expect
这样的扩展,其中编译器供应商通过使用这些保留名称来避免与您的标识符(由您的代码 声明 )发生任何冲突。即使是标准也将它们用于 __attribute__
之类的事情,以确保在添加新功能时不会破坏现有(合法)代码。
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1882
Each identifier that contains a double understore __ or begins with an underscore followed by an uppercase letter is reserved to the implementation for any use.
任意使用。 (在应用缺陷修复之前和之后都会出现类似的文本)
__cplusplus
由标准定义。 _ALGORITHM
由标准保留,供实现使用。这些看起来很不一样? (标准的两个部分确实存在冲突,其中一个部分声明 __cplusplus
保留用于任何用途,另一个部分专门使用它,但我认为该冲突的赢家很明显)。
根据标准,_ALGORITHM
标识符可以用作表示 "replace this source code with hard drive deleting code" 的预处理步骤的一部分。它的存在(在预处理之前或之后)足以完全改变您的程序行为。
现在这不太可能,但我认为这不会导致不符合要求的实施。这只是实施质量的问题。
实现可以自由记录和定义 _ALGORITHM
的含义。例如,它可以记录它是 <algorithm>
的头文件保护,并指示是否已包含该头文件。将您当前的 <algorithm>
实施作为文档处理可能会走得很远。
我猜想在 C 模式下使用 __cplusplus
在技术上 "just as bad" 与使用 _ALGORITHM
一样,但这个问题是 c++ question, not a c question. I haven't delved into the c 标准来寻找关于它的引用.
从历史上看,使用此类标记的目的 "undefined behavior" 是编译器可以自由地将他们想要的任何含义附加到 C 标准中未定义的任何此类标记。例如,在某些嵌入式处理器上,使用 __xdata 作为变量的存储空间 class 会要求将其存储在 RAM 区域,该区域的访问速度比普通变量存储区域慢,但是大得多。在该系列的典型处理器上,"normal" 变量的存储将被限制在大约 100 字节,但扩展数据变量的存储可能要大得多——高达 64K。该标准基本上没有说明允许编译器对此类指令执行的操作,尽管通常(我不确定标准是否强制执行此行为,尽管我不知道编译器违反了它)此类标记通常在代码中被忽略使用 #if
或类似指令禁用。
一些库的头文件将以两个下划线开头的内容开始它们自己的内部标识符,但包含编译器不太可能出于任何目的使用的模式(例如 Foozle 库的版本 23 可能在其标识符之前使用 __FZ23
)。对于未来的编译器来说,将以 __FZ23 开头的标识符用于其他目的是完全合法的,如果发生这种情况,则需要更改 Foozle 库以使用其他东西。但是,如果主要的编译器升级很可能出于其他原因需要重写 Foozle 库,那么与标识符与外部代码冲突的风险相比,这种风险可能是可以接受的。
另请注意,某些针对需要 __
指令的处理器的项目头文件可能会在为其他处理器编译时有条件地使用这些名称定义宏,例如:
#ifndef USE_XDATA
#define __XDATA
#endif
虽然更好的模式通常是:
#ifdef USE_XDATA
#define XDATA __XDATA
#else
#define XDATA
#endif
编写新代码时,后一种模式通常更好,但前一种模式有时在调整在需要 __XDATA 的平台上编写的现有代码时可能很有用,这样它就可以同时用于需要 __XDATA 的平台use/require 该指令和在没有该指令的平台上。
是否合法是当地法律的问题。它是否意味着什么,如果是,是什么,是语言定义的问题。当您使用为实现保留的名称时,您的程序的行为是未定义的。这意味着语言定义不会告诉您程序的作用。仅此而已。如果您使用的编译器记录了特定保留标识符的作用,那么您可以将该标识符与该编译器一起使用。如果您搜索 headers 并猜测各种 un-documented 标识符的含义,您也许可以使用它们,但是如果您的代码在后续更新更改某些内容时中断,请不要感到惊讶。
不要挂断 __cplusplus
。它是核心语言,关于双下划线等的东西是库。如果这不能令人信服,就认为这是一个小故障。你可以在 C++ 程序中使用 __cplusplus
;它的含义很明确。
[cpp.predefined]中的名称不同。那些具有指定的含义,因此实现不能保留它们以供任何使用,并且在程序中使用它们具有明确定义的可移植含义。使用像 _ALGORITHM 示例这样的特定于实现的标识符是不正确的,因为它违反了 shall-rule。
是的,我完全了解库规范使用 "shall" 表示 "this is a requirement on user code, and violations are UB, not ill-formed" 的多个示例。
关于它是 UB 还是实现定义的,运行宁一个格式错误的程序会导致 UB。标准措辞明确表示程序格式错误,如果实现仍然选择接受程序并且 运行 它会发生 UB。
因此,如果程序使用标识符 _ALGORITHM,则该程序格式错误,并且 运行ning 这样的程序是 UB,但这并不意味着它在以下实现上不能正常工作使用 _ALGORITHM 作为 include guard,也不意味着它不能在不支持的实现上正常工作。
如果用户担心这种格式错误和潜在的 UB,并表示用户希望编写可移植的 C++,则不应在可移植的 C++ 程序中使用保留标识符。如果用户接受不管标准是否禁止这样的使用,没有实际的实现会擦除你的硬盘,他们可以自由地使用这些保留的标识符,但按照标准的规定,这样的使用仍然是错误的。
在std-proposals列表中,给出了以下代码:
#include <vector>
#include <algorithm>
void foo(const std::vector<int> &v) {
#ifndef _ALGORITHM
std::for_each(v.begin(), v.end(), [](int i){std::cout << i; }
#endif
}
为了这个问题的目的,让我们忽略为什么给出该代码以及为什么这样写(因为有一个很好的理由,但它在这里无关紧要)。它假设 _ALGORITHM
是标准 header <algorithm>
内部的 header 守卫,与某些已知的标准库实现一起提供。这里没有便携性的内在意图。
现在,_ALGORITHM
当然是保留名称,根据:
[C++11: 2.11/3]:
In addition, some identifiers are reserved for use by C++ implementations and standard libraries (17.6.4.3.2) and shall not be used otherwise; no diagnostic is required.
[C++11: 17.6.4.3.2/1]:
Certain sets of names and function signatures are always reserved to the implementation:
- Each name that contains a double underscore
_ _
or begins with an underscore followed by an uppercase letter (2.12) is reserved to the implementation for any use.- Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.
我一直觉得这段话的目的是防止程序员定义/变异/取消定义 符合上述标准的名称,以便标准库实现者可以使用这些名称而不必担心与客户端代码发生冲突。
但是,在 std-proposals 列表中,有人声称此代码本身就是 ill-formed,仅用于引用此类保留名称。我现在可以看到 [C++11: 2.11/3]:
中的短语“不得以其他方式使用”的使用可能确实暗示了这一点。
例如,给出的一个实际理由是宏 _ALGORITHM
可以扩展为一些擦除硬盘驱动器的代码。但是,考虑到该规则的可能意图,我认为这种可能性更多地与 _ALGORITHM
名称的明显 implementation-defined* 性质有关,而与它的存在关系不大引用它是完全非法的。
* “implementation-defined”是英语语言意义上的,而不是短语的 C++ 标准意义
我想说的是,只要我们对获得 implementation-defined 结果感到高兴,并且我们应该研究该宏对我们的实施意味着什么(如果它存在的话!) , 如果我们不尝试修改它,那么引用这样的宏本质上不应该是非法的。
例如,到处使用如下代码来区分编译为 C 的代码和编译为 C++ 的代码:
#ifdef __cplusplus
extern "C" {
#endif
而且我从来没有听到过对此的抱怨。
那么,你怎么看? “不得以其他方式使用”包括简单地写这样一个名字吗?又或者可能不打算这么严格(这可能指向调整标准措辞的机会)?
它是否合法是特定于实现(和特定于标识符)的。
当标准授予实施使用这些名称的唯一权利时,这包括使这些名称在用户代码中可用的权利。如果一个实现这样做了,那就太好了。
但是,如果实施没有明确赋予您权利,那么从 "shall not be used otherwise" 可以清楚地看出标准没有,并且您有未定义的行为。
重要的部分是"reserved to the implementation"。这意味着编译器供应商可能会使用这些名称甚至记录它们。然后,您的代码可以使用记录的这些名称。这通常用于像 __builtin_expect
这样的扩展,其中编译器供应商通过使用这些保留名称来避免与您的标识符(由您的代码 声明 )发生任何冲突。即使是标准也将它们用于 __attribute__
之类的事情,以确保在添加新功能时不会破坏现有(合法)代码。
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1882
Each identifier that contains a double understore __ or begins with an underscore followed by an uppercase letter is reserved to the implementation for any use.
任意使用。 (在应用缺陷修复之前和之后都会出现类似的文本)
__cplusplus
由标准定义。 _ALGORITHM
由标准保留,供实现使用。这些看起来很不一样? (标准的两个部分确实存在冲突,其中一个部分声明 __cplusplus
保留用于任何用途,另一个部分专门使用它,但我认为该冲突的赢家很明显)。
根据标准,_ALGORITHM
标识符可以用作表示 "replace this source code with hard drive deleting code" 的预处理步骤的一部分。它的存在(在预处理之前或之后)足以完全改变您的程序行为。
现在这不太可能,但我认为这不会导致不符合要求的实施。这只是实施质量的问题。
实现可以自由记录和定义 _ALGORITHM
的含义。例如,它可以记录它是 <algorithm>
的头文件保护,并指示是否已包含该头文件。将您当前的 <algorithm>
实施作为文档处理可能会走得很远。
我猜想在 C 模式下使用 __cplusplus
在技术上 "just as bad" 与使用 _ALGORITHM
一样,但这个问题是 c++ question, not a c question. I haven't delved into the c 标准来寻找关于它的引用.
从历史上看,使用此类标记的目的 "undefined behavior" 是编译器可以自由地将他们想要的任何含义附加到 C 标准中未定义的任何此类标记。例如,在某些嵌入式处理器上,使用 __xdata 作为变量的存储空间 class 会要求将其存储在 RAM 区域,该区域的访问速度比普通变量存储区域慢,但是大得多。在该系列的典型处理器上,"normal" 变量的存储将被限制在大约 100 字节,但扩展数据变量的存储可能要大得多——高达 64K。该标准基本上没有说明允许编译器对此类指令执行的操作,尽管通常(我不确定标准是否强制执行此行为,尽管我不知道编译器违反了它)此类标记通常在代码中被忽略使用 #if
或类似指令禁用。
一些库的头文件将以两个下划线开头的内容开始它们自己的内部标识符,但包含编译器不太可能出于任何目的使用的模式(例如 Foozle 库的版本 23 可能在其标识符之前使用 __FZ23
)。对于未来的编译器来说,将以 __FZ23 开头的标识符用于其他目的是完全合法的,如果发生这种情况,则需要更改 Foozle 库以使用其他东西。但是,如果主要的编译器升级很可能出于其他原因需要重写 Foozle 库,那么与标识符与外部代码冲突的风险相比,这种风险可能是可以接受的。
另请注意,某些针对需要 __
指令的处理器的项目头文件可能会在为其他处理器编译时有条件地使用这些名称定义宏,例如:
#ifndef USE_XDATA
#define __XDATA
#endif
虽然更好的模式通常是:
#ifdef USE_XDATA
#define XDATA __XDATA
#else
#define XDATA
#endif
编写新代码时,后一种模式通常更好,但前一种模式有时在调整在需要 __XDATA 的平台上编写的现有代码时可能很有用,这样它就可以同时用于需要 __XDATA 的平台use/require 该指令和在没有该指令的平台上。
是否合法是当地法律的问题。它是否意味着什么,如果是,是什么,是语言定义的问题。当您使用为实现保留的名称时,您的程序的行为是未定义的。这意味着语言定义不会告诉您程序的作用。仅此而已。如果您使用的编译器记录了特定保留标识符的作用,那么您可以将该标识符与该编译器一起使用。如果您搜索 headers 并猜测各种 un-documented 标识符的含义,您也许可以使用它们,但是如果您的代码在后续更新更改某些内容时中断,请不要感到惊讶。
不要挂断 __cplusplus
。它是核心语言,关于双下划线等的东西是库。如果这不能令人信服,就认为这是一个小故障。你可以在 C++ 程序中使用 __cplusplus
;它的含义很明确。
[cpp.predefined]中的名称不同。那些具有指定的含义,因此实现不能保留它们以供任何使用,并且在程序中使用它们具有明确定义的可移植含义。使用像 _ALGORITHM 示例这样的特定于实现的标识符是不正确的,因为它违反了 shall-rule。
是的,我完全了解库规范使用 "shall" 表示 "this is a requirement on user code, and violations are UB, not ill-formed" 的多个示例。
关于它是 UB 还是实现定义的,运行宁一个格式错误的程序会导致 UB。标准措辞明确表示程序格式错误,如果实现仍然选择接受程序并且 运行 它会发生 UB。
因此,如果程序使用标识符 _ALGORITHM,则该程序格式错误,并且 运行ning 这样的程序是 UB,但这并不意味着它在以下实现上不能正常工作使用 _ALGORITHM 作为 include guard,也不意味着它不能在不支持的实现上正常工作。
如果用户担心这种格式错误和潜在的 UB,并表示用户希望编写可移植的 C++,则不应在可移植的 C++ 程序中使用保留标识符。如果用户接受不管标准是否禁止这样的使用,没有实际的实现会擦除你的硬盘,他们可以自由地使用这些保留的标识符,但按照标准的规定,这样的使用仍然是错误的。