关于命名约定:在C++标识符中使用下划线的规则是什么?

What are the rules about using an underscore in a C++ identifier?

C++中常用的是用某种前缀命名成员变量来表示它们是成员变量,而不是局部变量或参数。如果您来自MFC背景,您可能会使用m_foo。我偶尔也会看到myFoo

C(或者可能只是.NET)似乎建议只使用下划线,就像在_foo中那样。这是C++标准允许的吗?


规则(在C++ 11中没有改变):

  • 保留在任何范围内,包括用作实现宏:
    • 以下划线开头并紧跟大写字母的标识符
    • 包含相邻下划线(或"双下划线")的标识符
  • 在全局命名空间中保留:
    • 以下划线开头的标识符
  • 另外,std名称空间中的所有内容都是保留的。(不过,您可以添加模板专门化。)

从2003 C++标准:

17.4.3.1.2 Global names [lib.global.names]

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.11) 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.165

165) Such names are also reserved in namespace ::std (17.4.3.1).

因为C++是基于C标准(1.1/2,C++ 03),而C99是一个规范的引用(1.2/1,C++ 03),它们也适用于1999 C标准:

7.1.3 Reserved identifiers

Each header declares or defines all identifiers listed in its associated subclause, and
optionally declares or defines identifiers listed in its associated future library directions subclause and identifiers which are always reserved either for any use or for use as file scope identifiers.

  • All identifiers that begin with an underscore and either an uppercase letter or another
    underscore are always reserved for any use.
  • All identifiers that begin with an underscore are always reserved for use as identifiers
    with file scope in both the ordinary and tag name spaces.
  • Each macro name in any of the following subclauses (including the future library
    directions) is reserved for use as specified if any of its associated headers is included;
    unless explicitly stated otherwise (see 7.1.4).
  • All identifiers with external linkage in any of the following subclauses (including the
    future library directions) are always reserved for use as identifiers with external
    linkage.154
  • Each identifier with file scope listed in any of the following subclauses (including the
    future library directions) is reserved for use as a macro name and as an identifier with
    file scope in the same name space if any of its associated headers is included.

No other identifiers are reserved. If the program declares or defines an identifier in a
context in which it is reserved (other than as allowed by 7.1.4), or defines a reserved
identifier as a macro name, the behavior is undefined.

If the program removes (with #undef) any macro definition of an identifier in the first
group listed above, the behavior is undefined.

154) The list of reserved identifiers with external linkage includes errno, math_errhandling, setjmp, and va_end.

其他限制可能适用。例如,POSIX标准保留了许多可能在正常代码中显示的标识符:

  • 以大写字母E开头的名称,后跟数字或大写字母:
    • 可用于其他错误代码名称。
  • isto开头,后跟小写字母的名称。
    • 可用于其他字符测试和转换功能。
  • LC_开头,后跟一个大写字母的名称
    • 可用于指定区域设置属性的其他宏。
  • 保留所有以fl后缀的现有数学函数的名称。
    • 分别对float和long双参数进行操作的相应函数。
  • 保留以SIG开头,后跟大写字母的名称。
    • 其他信号名称。
  • 保留以SIG_开头,后跟大写字母的名称。
    • 其他信号动作。
  • 保留以strmemwcs开头,后跟小写字母的名称。
    • 用于其他字符串和数组函数。
  • 保留以PRISCN开头,后跟任何小写字母或X的名称。
    • 对于其他格式说明符宏
  • _t结尾的名称保留
    • 用于其他类型名。

虽然现在将这些名称用于您自己的目的可能不会造成问题,但它们确实会增加与该标准未来版本冲突的可能性。

就个人而言,我只是不以下划线开头标识符。我的新规则:不要在任何地方使用双下划线,这很容易,因为我很少使用下划线。

在对本文进行研究之后,我不再使用_t结束标识符。因为这是由POSIX标准保留的。

任何以_t结尾的标识符的规则都让我大吃一惊。我认为这是一个POSIX标准(还不确定),寻求澄清和官方章节。这是来自GNUlibtool手册,列出了保留名称。

CESARB提供了以下到POSIX2004保留符号的链接,并指出许多其他保留前缀和后缀…可以在那里找到。这个这里定义了POSIX2008保留符号。这些限制比上述限制更为微妙。


避免名字冲突的规则都在C++标准中(参见Stroustrup书),并由C++大师(萨特等)提到。

个人规则

因为我不想处理案件,想要一个简单的规则,我设计了一个既简单又正确的个人规则:

在命名符号时,如果您:

  • 从不以下划线开头符号
  • 不要用两个连续下划线来命名符号。

当然,将代码放在一个唯一的名称空间中也有助于避免冲突(但不会保护恶意宏)

一些实例

(我使用宏是因为它们对C/C++符号的代码污染更大,但它可以是从变量名到类名的任何东西)。

1
2
3
4
5
6
#define _WRONG
#define __WRONG_AGAIN
#define RIGHT_
#define WRONG__WRONG
#define RIGHT_RIGHT
#define RIGHT_x_RIGHT

C++ 0x草稿

来自n3242.pdf文件(我希望最终标准文本相似):

17.6.3.3.2 Global names [global.names]

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.

而且:

17.6.3.3.5 User-defined literal suffixes [usrlit.suffix]

Literal suffix identifiers that do not start with an underscore are reserved for future standardization.

最后一个子句很容易混淆,除非您认为如果没有在全局命名空间中定义名称,那么以一个下划线开头并后跟一个小写字母的名称是可以的…


来自MSDN:

Use of two sequential underscore characters ( __ ) at the beginning of an identifier, or a single leading underscore followed by a capital letter, is reserved for C++ implementations in all scopes. You should avoid using one leading underscore followed by a lowercase letter for names with file scope because of possible conflicts with current or future reserved identifiers.

这意味着您可以使用一个下划线作为成员变量前缀,只要它后面跟一个小写字母。

这显然是从C++标准的第174.3.1.2节获得的,但是我找不到一个在线的完整标准的原始来源。

另请参见此问题。


至于问题的另一部分,通常在变量名末尾加下划线,以避免与任何内部内容冲突。

我甚至在类和名称空间中都这样做,因为这样我只需要记住一个规则(与"在全局范围内名称的末尾,在其他地方名称的开头"相比)。


是的,下划线可以在标识符的任何地方使用。我相信规则是:A-Z,A-Z,第一个字符中的任何一个,后面的字符中的+0-9。

下划线前缀在C代码中很常见——单个下划线表示"private",而双下划线通常是为编译器保留的。