#pragma once是C++11标准的一部分吗?

159
传统上,在C++中避免多个头文件包含的标准和便携方法是使用预处理器指令方案,也称为宏保护方案(见下面的代码片段),其中包括#ifndef-#define-#endif。请注意,保留HTML标签。
#ifndef MY_HEADER_HPP
#define MY_HEADER_HPP
...
#endif

在大多数实现/编译器中(见下图),有一种更“优雅”的替代宏防护方案的选择,称为#pragma once。与宏防护方案相比,#pragma once具有几个优点,包括更少的代码、避免名称冲突以及有时提高编译速度。

enter image description here

在做一些研究后,我发现虽然几乎所有已知的编译器都支持#pragma once指令,但对于#pragma once指令是否是C++11标准的一部分还存在一些不清晰的问题。

问题:

  • 请问有人能澄清#pragma once指令是否是C++11标准的一部分吗?
  • 如果它不是C++11标准的一部分,是否有计划在以后的版本(例如C++14或更高版本)中包含它?
  • 如果有人能进一步阐述使用宏保护和#pragma once两种技术的优缺点,那就太好了。

10
顺便提一下,使用双下划线作为头文件保护符号是被标准禁止的。标准将以双下划线开头的所有符号(以及其他符号)保留给实现使用。 - Matteo Italia
10
使用一个前导下划线后跟一个大写字母也是被禁止的。其次,这里的混浊在哪里?我只看到编译器支持,没有人声称它是标准的一部分吗? - Yakk - Adam Nevraumont
1
对于第三个要点,请查看相关问题:Is #pragma once a safe include guard? 它提供了一个情况,其中头文件保护起作用,但 #pragma once 通常不起作用。 - AliciaBytes
1
在回答问题的过程中,可能是重复的,因为它没有提到C++11。 - Yakk - Adam Nevraumont
4
虽然这并没有被写进任何官方文件,但你可以把它视为事实上的标准。 - Siyuan Ren
显示剩余2条评论
2个回答

123

#pragma once不是标准语法。它是广泛使用的(但不是普遍)扩展,可以用于:

  • 仅限于您的可移植性问题有限,并且
  • 您可以确保所有包含文件始终在本地磁盘上。

它曾经考虑纳入标准,但被拒绝了,因为它不能可靠地实现。(当您拥有通过多个不同远程挂载访问的文件时会出现问题。)

很容易确保单个开发内没有包含保护冲突。对于可能由许多不同开发使用的库,明显的解决方案是在创建时为包含保护符生成许多随机字符。(一个好的编辑器可以在打开新的头文件时自动为您执行此操作。)但即使没有这样做,我也还没有遇到过任何库之间冲突的问题。


12
不仅仅是远程挂载,硬链接、软链接和subst构造(在Windows上)也会使情况变得非常混乱。 - Tonny
57
编译器为什么不能使用SHA-1或MD5校验和来识别文件? - Sergey
40
如果每个主要的编译器都支持某个东西,我真的看不出不将其纳入标准的意义所在。实际上,与此相比,一些已经在标准中的东西支持度要低得多。而且,在处理包含文件时,文件名冲突已经是一个巨大的问题了,所以抱怨边缘问题似乎有点傻。如果对于一般的被 #include 的头文件概念也要求百分之百无问题,那就更好了。 - T.E.D.
51
如果您的代码通过符号链接或奇怪的挂载从不同位置包含某些文件,则它已经不可移植。因此,认为pragma once无法实现本质上不可移植的内容(甚至不应考虑)的论点是C++颠倒世界的另一种荒谬想法。 - mip
9
我同意符号链接是操作系统的特性,超出了C++语言的范围。因此,问题是为什么C++委员会应该考虑不属于语言范畴的东西?在我看来,试图保证不是他们职责的事情毫无意义。DOS只支持每个文件名8+3个字符,但没有人争论说必须删除#include,因为可以盲目地滥用该指令。#pragma once没有以任何方式限制可移植性,只要您不利用符号链接来破坏编译。 - mip
显示剩余12条评论

36

标准第 §16.6 节(N3936草案)描述了 #pragma 指令:

形式为

# pragma pp-tokensopt new-line

使用 #pragma 指令会导致实现以实现定义的方式进行行为。此行为可能导致翻译失败或者导致翻译器或生成的程序表现出不符合规范的行为。任何未被实现认可的编译指示都将被忽略。

基本上,#pragma once#pragma 指令在具体实现中的一个实例,而且它目前还不是标准。它通常被大多数主要编译器广泛支持,包括GCCClang,因此有时建议使用它来避免重复包含的样板代码。


12
注意,你可以同时使用#pragma#define来定义头文件保护。 - Yakk - Adam Nevraumont
20
“任何实现未识别的编译指示(pragma)都会被忽略。”这句话的意思是,如果编译器没有识别出某个特定的编译指示,那么它会跳过它。这是否意味着消息“警告:未识别的编译指示”不符合规范? - rodrigo
6
“因此,这是避免包含保护宏模板的推荐方法”——这是一个非常大胆的陈述。虽然这是一种非标准的方式,但使用它的好处很少,并且在我的经验中几乎没有实际意义,因此我不得不撤回我的+1支持。 - Alex
21
如果有人编写了#define头文件保护宏,那么他/她就没有理由再编写#pragma once - Nawaz
7
一个编译器可以缓存每个已经使用了 #pragma once 的文件(通过路径),如果再次使用 #include 引入同一个文件,编译器会跳过 #include 操作(甚至不打开文件)。gcc也是这样做的,但非常非常脆弱。#pragma 比较容易实现,头文件守卫比较难实现。 - Yakk - Adam Nevraumont
显示剩余12条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接