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

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

本文关键字:下划线 规则 是什么 C++ 标识符 命名约定      更新时间:2023-10-16

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

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

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

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

2003 C++标准:

17.4.3.1.2全局名称[lib.Global.names]

某些名称和函数签名集总是保留给实现:

  • 每个包含双下划线(__)或以下划线后跟大写字母(2.11)开头的名称都保留给实现以供任何使用
  • 每个以下划线开头的名称都保留给实现,用作全局命名空间中的名称165

165)此类名称也保留在命名空间::std(17.4.3.1)中。

因为C++基于C标准(1.1/2,C++03),C99是一个规范性参考(1.2/1,C++03

7.1.3保留标识符

每个标题声明或定义其相关子条款中列出的所有标识符,以及可选地声明或定义在其相关联的未来库方向子条款中列出的标识符以及始终保留用于任何用途或用作文件范围标识符的标识符。

  • 所有以下划线和大写字母或其他字母开头的标识符下划线总是保留以供任何使用
  • 所有以下划线开头的标识符始终保留用作标识符在普通名称空间和标记名称空间中都有文件作用域
  • 以下任何子条款中的每个宏名称(包括将来的库directions)被保留以在包括其任何相关联的报头的情况下按指定使用;除非另有明确说明(见7.1.4)
  • 以下任何子条款中具有外部链接的所有标识符(包括未来的库方向)始终保留用作外部的标识符链接154
  • 以下任何子条款(包括未来库方向)保留用作宏名称和标识符如果包含任何相关联的标头,则在同一名称空间中的文件作用域

不保留任何其他标识符。如果程序在保留它的上下文(7.1.4允许的除外),或定义保留标识符作为宏名称,行为是未定义的。

如果程序删除(使用#undef)第一个组,行为未定义。

154)具有外部链接的保留标识符的列表包括errnomath_errhandlingsetjmpva_end

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

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

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


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

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

关于任何以_t结尾的标识符的规则让我非常惊讶。我认为这是一个POSIX标准(还不确定),寻求澄清和正式的章节和诗句。这是GNU libtool手册中列出的保留名称。

CesarB提供了以下到POSIX 2004保留符号的链接,并注意到"许多其他保留前缀和后缀。。。可以在那里找到"。这个此处定义了POSIX 2008保留符号。这些限制比上面的更为微妙。

避免名称冲突的规则既在C++标准中(见Stroustrup书),也在C++大师(Sutter等)中提到

个人规则

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

在命名符号时,如果您:,您将避免与编译器/OS/标准库发生冲突

  • 永远不要以下划线开头
  • 永远不要给里面有两个连续下划线的符号命名

当然,将代码放在一个唯一的命名空间中也有助于避免冲突(但不能防止恶意宏)

一些例子

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

#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]

某些名称和函数签名集总是保留给实现:

--每个包含双下划线__或以下划线后跟大写字母(2.12)开头的名称都保留给实现以供任何使用。

--每个以下划线开头的名称都保留给实现,用作全局命名空间中的名称。

还有:

17.6.3.3.5用户定义的文字后缀[usrlit.suffix]

不以下划线开头的文字后缀标识符保留用于将来的标准化。

最后一个子句很令人困惑,除非您认为如果没有在全局命名空间中定义,那么以一个下划线开头并后跟小写字母的名称就可以了。。。

来自MSDN:

在标识符的开头使用两个连续的下划线字符(__),或者在一个前导下划线后面跟着一个大写字母,这是为所有作用域中的C++实现保留的。对于具有文件作用域的名称,应避免使用一个前导下划线和一个小写字母,因为这可能与当前或将来的保留标识符冲突。

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

这显然取自C++标准的第17.4.3.1.2节,但我在网上找不到完整标准的原始来源。

另请参阅此问题。

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

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

是的,下划线可以在标识符中的任何位置使用。我相信规则是:第一个字符中的a-z、a-z、_中的任何一个,以及后面字符的+0-9。

下划线前缀在C代码中很常见——单下划线表示"私有",双下划线通常保留给编译器使用。