996

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

C#(或者可能只是 .NET)似乎建议只使用下划线,如_foo. 这是 C++ 标准允许的吗?

4

5 回答 5

916

规则(在 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),这些也适用于 1999 年的 C 标准:

7.1.3 保留标识符

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

  • 以下划线和大写字母或另一个下划线开头的所有标识符始终保留用于任何用途。
  • 所有以下划线开头的标识符始终保留用作普通和标记名称空间中具有文件范围的标识符。
  • 如果包含任何关联的头文件,则以下任何子条款(包括未来的库方向)中的每个宏名称都保留用于指定用途;除非另有明确说明(见 7.1.4)。
  • 以下任何子条款(包括未来的库方向)中具有外部链接的所有标识符始终保留用作具有外部链接的标识符。154
  • 如果包含任何关联的标头,则保留在以下任何子条款(包括未来的库方向)中列出的具有文件范围的每个标识符用作宏名称和具有相同名称空间中的文件范围的标识符。

没有保留其他标识符。如果程序在保留标识符的上下文中声明或定义标识符(7.1.4 允许的除外),或将保留标识符定义为宏名称,则行为未定义。

如果程序删除(用#undef)上面列出的第一组中标识符的任何宏定义,则行为未定义。

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

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

  • 以大写字母开头的名称E后跟数字或大写字母:
    • 可用于附加错误代码名称。
  • 以小写字母开头isto后跟小写字母的 名称
    • 可用于额外的字符测试和转换功能。
  • LC_以大写字母开头的 名称
    • 可用于指定语言环境属性的附加宏。
  • 所有现有数学函数的名称后缀为forl保留
    • 分别用于对 float 和 long double 参数进行操作的相应函数。
  • SIG以大写字母开头的 名称被保留
    • 附加信号名称。
  • SIG_以大写字母开头的 名称被保留
    • 额外的信号动作。
  • str保留以、memwcs后跟小写字母开头的 名称
    • 用于附加字符串和数组函数。
  • 以任何小写字母开头PRISCN后跟或X保留 的名称
    • 用于附加格式说明符宏
  • 以结尾的名称_t是保留的
    • 其他类型名称。

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


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

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

关于任何以结尾的标识符的规则_t让我很惊讶。我认为这是一个 POSIX 标准(尚不确定),正在寻找澄清和官方章节。这来自GNU libtool 手册,列出了保留名称。

CesarB 提供了以下指向POSIX 2004保留符号的链接,并指出“可以在此处找到许多其他保留的前缀和后缀……”。POSIX 2008保留符号在此处定义。 这些限制比上述限制更细微。

于 2008-10-23T07:08:41.280 回答
213

避免名称冲突的规则都在 C++ 标准中(参见 Stroustrup 书)和 C++ 大师(Sutter 等)提到的。

个人规则

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

命名符号时,如果您满足以下条件,您将避免与编译器/操作系统/标准库发生冲突:

  • 永远不要以下划线开头的符号
  • 永远不要用两个连续的下划线命名符号。

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

一些例子

(我使用宏是因为它们对 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]

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

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

于 2008-10-23T07:27:12.287 回答
46

来自MSDN

在标识符的开头使用两个连续的下划线字符 ( __ ) 或单个前导下划线后跟一个大写字母,保留用于所有范围内的 C++ 实现。您应该避免使用一个前导下划线后跟一个小写字母来表示具有文件范围的名称,因为可能与当前或将来的保留标识符发生冲突。

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

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

另请参阅此问题

于 2008-10-23T07:06:32.703 回答
27

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

我什至在类和名称空间内也这样做,因为我只需要记住一个规则(与“在全局范围内的名称末尾,以及其他任何地方的名称开头”相比)。

于 2008-11-14T20:03:28.710 回答
1

是的,下划线可以在标识符的任何地方使用。我相信规则是:第一个字符中的任何 az、AZ、_ 以及以下字符中的 +0-9。

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

于 2008-10-23T07:05:58.040 回答