【问题标题】:What are the rules about using an underscore in a C++ identifier?在 C++ 标识符中使用下划线的规则是什么?
【发布时间】:2022-12-14 18:13:10
【问题描述】:

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

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

【问题讨论】:

标签: c++ naming-conventions standards c++-faq


【解决方案1】:

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

  • 在任何范围内保留,包括用作 implementation 宏:
    • 以下划线开头紧跟大写字母的标识符
    • 包含相邻下划线(或“双下划线”)的标识符
  • 在全局命名空间中保留:
    • 以下划线开头的标识符
  • 此外,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 保留标识符

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

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

没有保留其他标识符。如果程序在一个 它被保留的上下文(7.1.4 允许的除外),或定义了一个保留的 标识符作为宏名称,行为未定义。

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

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

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

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

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


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

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

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

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

【讨论】:

  • C++ 标准不会“导入”C 标准,对吗?据我所知,它们导入某些标头,但不导入整个语言或命名规则。但是,是的,_t 也让我感到惊讶。但是既然是C,就只能适用于全局的ns。在我阅读它时应该可以安全地在类中使用 _t
  • C++ 标准不“导入”C 标准。它参考C标准。 C++ 库介绍说“该库还提供了标准 C 库的功能”。它通过包含具有适当更改的 C 标准库的标头来实现这一点,而不是通过“导入”它。 C++ 标准有一套自己的规则来描述保留名称。如果一个在 C 中保留的名称应该在 C++ 中保留,那就是说这个的地方。但是 C++ 标准并没有这么说。所以我不相信 C 中保留的东西在 C++ 中保留——但我很可能是错的。
  • 这是我发现的关于“_t”问题的内容:n1256 (C99 TC3) 说:“Typedef 名称以 int 或 uint 开头并以 _t 结尾”是保留的。我认为这仍然允许使用像“foo_t”这样的名称——但我认为这些名称随后会被 POSIX 保留。
  • 所以'tolerance'是由POSIX保留的,因为它以'to'+小写字母开头?我敢打赌很多代码都违反了这条规则!
  • @LokiAstari,“C++ 标准是根据 C 标准定义的。基本上它说 C++ 是具有这些差异和添加的 C。“废话!C++只引用了[basic.fundamental]和库中的C标准。如果你说的是真的,C++哪里说_Bool_Imaginary在C++中不存在?C++语言定义明确地,不是对 C 的“编辑”,否则标准可能会更短!
【解决方案2】:

避免名称冲突的规则既存在于 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]

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

这最后一个条款令人困惑,除非你认为一个以一个下划线开头,后跟一个小写字母的名字是可以的,如果不是在全局命名空间中定义...

【讨论】:

  • @Meysam:__WRONG_AGAIN__包含两个连续的下划线(两个在开头,两个在结尾),所以根据标准这是错误的。
  • @BЈовић : WRONG__WRONG 包含两个连续的下划线(中间两个),所以根据标准这是错误的
  • 将您的代码放在唯一的命名空间中也有助于避免冲突: 但这仍然不够,因为无论范围如何,标识符都可能与关键字冲突(例如 GCC 的 __attribute__)。
  • 为什么有两个连续的下划线会出现问题在中间按照标准?用户定义的文字后缀适用于1234567L4.0f 等文字值; IIRC 这指的是 ohttp://en.cppreference.com/w/cpp/language/user_literal
  • Why is there any problem of having two consecutive underscores in the middle according to the standard? 因为标准说那些是保留的。这不是一个建议关于风格的好坏。它是决定从标准。他们为什么这样决定?我猜第一批编译器在标准化之前已经非正式地使用了这样的约定。
【解决方案3】:

来自MSDN

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

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

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

另见this question

【讨论】:

  • 我在 n3092.pdf(C++0x 标准草案)的“17.6.3.3.2 全局名称”部分找到了类似的文本
  • 有趣的是,这似乎是唯一对问题有直接、简洁答案的答案。
  • @hyde:实际上,它不是,因为它跳过了在全局命名空间中没有任何带有前导下划线的标识符的规则。参见Roger's answer。我会非常谨慎地引用 MS VC 文档作为 C++ 标准的权威。
  • @sbi 我指的是“你可以使用单个下划线作为成员变量前缀,只要它后面跟一个小写字母”在这个答案中,它直接简洁地回答了问题文本中的问题,而没有淹没在文本墙中。
  • 首先,我仍然认为没有任何提示表明同一规则不适用于全局命名空间是一个失败。更糟糕的是,相邻的下划线不仅在开头,而且任何地方在,一个标识符。所以这个答案不仅仅是遗漏了一个事实,而且实际上至少提出了一个积极错误的主张。正如我所说,除非问题完全是关于 VC,否则我不会参考 MSVC 文档。
【解决方案4】:

至于问题的另一部分,通常将下划线放在结尾变量名称不与任何内部冲突。

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

【讨论】:

    【解决方案5】:

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

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

    【讨论】:

    • 它们在图书馆很常见。它们不应该在用户代码中常见。
    • 人们用 C 编写库,你知道的。
    • “是的,下划线可以在标识符中的任何地方使用。”这对于全局标识符是错误的。见Roger's answer
    • @sbi 根据 C 和 C++ 标准,是的,在语义上,保留带有前导下划线的全局标识符。尽管它们在语法上是有效的标识符,并且编译器不会阻止您将函数命名为_Foo,尽管这样做您依赖于非标准的实现细节,因此有可能让您的代码被未来版本的语言/标准破坏图书馆实施/操作系统。
    • @BenW:TTBOMK,C++ 标准只是说不允许使用下划线开头的全局标识符,而不区分语法和语义。 (还有任何以下划线开头后跟大写字母的标识符,以及带有两个连续下划线的标识符。)
    猜你喜欢
    • 2021-11-04
    • 2015-03-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-30
    • 2010-09-18
    • 1970-01-01
    • 2017-10-16
    • 2011-11-16
    相关资源
    最近更新 更多