在C++标识符中使用下划线有哪些规则?

Rog*_*mbe 906 c++ standards naming-conventions c++-faq

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

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

Mar*_*ork 822

规则(在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)与外部连接保留的标识符的列表包括errno,math_errhandling,setjmp,和va_end.

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

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

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


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

在对本文进行研究后,我不再使用_t POSIX标准保留标识符.

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

CesarB提供了以下指向POSIX 2004保留符号的链接,并注意到"可以在那里找到许多其他保留的前缀和后缀......".该 POSIX 2008保留符号在这里被定义.这些限制比上述限制更微妙.

  • 所以POSIX保留了'tolerance',因为它以'to'+小写字母开头?我打赌很多代码违反了这条规则! (56认同)
  • C++标准不"导入"C标准.它*引用*C标准.C++库介绍说"该库还提供了标准C库的功能".它通过将C标准库的标题包含适当的更改来实现,但不是通过"导入"它.C++标准有一组描述保留名称的规则.如果C中保留的名称应该在C++中保留,那么这就是说这个的地方.但是C++标准并没有这么说.所以我不相信C中保留的东西在C++中保留 - 但我可能错了. (27认同)
  • @LokiAstari,"_ C++标准是根据C标准定义的.基本上它说C++是C,带有这些差异和附加._"废话!C++仅引用[basic.fundamental]和库中的C标准.如果你说的是真的,C++在哪里说C++中不存在`_Bool`和`_Imaginary`?C++语言是明确定义的,而不是C语言的"编辑",否则标准可能会短得多! (23认同)
  • C++标准不会"导入"C语言,是吗?据我所知,他们导入某些标题,但不是整个语言或命名规则.但是,是的,那个人也让我感到惊讶.但由于它是C,它只能应用于全局ns.在我阅读它时,在类内部使用_t应该是安全的 (14认同)
  • 这是我发现的关于"_t"问题的内容:n1256(C99 TC3)说:"以int或uint开头并以_t结尾的Typedef名称"是保留的.我认为仍然允许使用像"foo_t"这样的名字 - 但我认为这些都是由POSIX保留的. (8认同)
  • @Sjoerd:可能.虽然我确信只要"lerance"不会成为可以应用于角色的真实动词,你就会没事.另请注意,它仅在全局范围(C)_或标准命名空间(C++)中保留,因此您可以使用此名称的函数变量而不破坏规则. (6认同)
  • @LokiAstari,这是描述语言范围的非常通用的语句,并不意味着C中的所有内容都被导入到C++中.C++语言(不是库)由其自己的标准精确定义,而不是通过引用另一个,除了[basic.fundamental]中的一个引用. (4认同)
  • 如主要文章中所述,'_t'后缀仅由POSIX标准而非C标准保留. (3认同)
  • 来自C++标准1.1.<quote> C++是一种基于C编程语言的通用编程语言,如ISO/IEC 9899:1990编程语言 - C(1.2)中所述.除了C提供的功能外,C++还提供了其他数据类型</ quote>.我对此的解读是,除非另有明确说明,否则C中保留的任何内容也保留在C++中. (2认同)
  • @MaximYegorushkin:没有违反规则。这些标识符是为实现保留的。getopt_long() 是编译器和标准库的 GNU 实现的一部分。 (2认同)
  • @MaxBarraclough 因此,如果你打破了这个条件,你的代码就是不合格的。如果我们然后阅读**第 4 节一般原则第 2.3 段**“如果程序包含违反不需要诊断的规则的行为,则本文档对该程序的实现不提出任何要求。” (2认同)

pae*_*bal 192

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

个人规则

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

命名符号时,如果您符合以下条件,则可避免与编译器/ OS /标准库冲突:

  • 永远不要用下划线开始一个符号
  • 永远不要在里面用两个连续的下划线命名.

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

一些例子

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

#define _WRONG
#define __WRONG_AGAIN
#define RIGHT_
#define WRONG__WRONG
#define RIGHT_RIGHT
#define RIGHT_x_RIGHT
Run Code Online (Sandbox Code Playgroud)

来自C++ 0x草案的摘录

n3242.pdf文件(我希望最终的标准文本类似):

17.6.3.3.2全局名称[global.names]

某些名称和函数签名集始终保留给实现:

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

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

但是也:

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

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

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

  • @Meysam:`__WRONG_AGAIN__`包含两个连续的下划线(两个在开头,两个在结尾),所以根据标准这是错误的. (9认同)
  • @BЈовић:`WRONG__WRONG`包含两个连续的下划线(中间两个),所以根据标准这是错误的 (7认同)
  • 怎么样`#define __WRONG_AGAIN__`?这也错了吗? (6认同)
  • `为什么按照标准中间有两个连续的下划线会有问题?`因为标准说那些是保留的。这不是关于好坏风格的**建议**。这是标准的**决定**。他们为什么决定这个?我猜第一批编译器在标准化之前已经非正式地使用了这些约定。 (4认同)
  • _在一个唯一的命名空间中输入你的代码也有助于避免冲突,但是这仍然不够,因为无论范围如何,标识符都可能与关键字冲突(例如GCC的`__attribute__`). (2认同)

Rog*_*mbe 35

来自MSDN:

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

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

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

另见这个问题.

  • @hyde:实际上,它不是,因为它正在跳过规则,在全局命名空间中没有带有前导下划线的任何标识符.参见[Roger的回答](http://stackoverflow.com/a/228797/140719).我非常谨慎地引用MS VC文档作为C++标准的权威. (9认同)
  • 有趣的是,这似乎是唯一一个对这个问题有直接,简洁答案的答案. (6认同)
  • 首先,我仍然认为缺少任何暗示同一规则不适用于全局命名空间失败.更糟糕的是,相邻的下划线不仅仅是在标识符的开头,而且*在任何地方*中都是禁止的.因此,这个答案不仅仅是省略了一个事实,而且实际上至少提出了一个积极的错误主张.正如我所说,除非问题完全是关于VC,否则提到MSVC文档是我不会做的. (5认同)
  • 我在n3092.pdf(C++ 0x标准草案)的章节中找到了类似的文字:"17.6.3.3.2全局名称" (2认同)

Max*_*ert 24

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

我甚至在类和名称空间内执行此操作,因为我只需要记住一条规则(与"在全局范围内的名称末尾,以及其他地方名称的开头"相比).