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

101*_*010 137 c++ macros header-files c++11 c++14

传统上,避免C++中多个头部包含的标准和可移植方法是使用#ifndef - #define - #endif预编译器指令方案,也称为宏保护方案(参见下面的代码片段).

#ifndef MY_HEADER_HPP
#define MY_HEADER_HPP
...
#endif
Run Code Online (Sandbox Code Playgroud)

然而,在大多数实现/编译器中(见下图),有一个更"优雅"的替代方案,其功能与调用的宏保护方案相同#pragma once.#pragma once与宏保护方案相比具有几个优点,包括更少的代码,避免名称冲突,有时提高编译速度.

在此输入图像描述

做了一些研究,我意识到尽管#pragma once几乎所有已知的编译器都支持指令,但是指令是否#pragma once是C++ 11标准的一部分还是存在紊乱.

问题:

  • 有人可以澄清#pragma once指令是否属于C++ 11标准的一部分吗?
  • 如果它不是C++ 11标准的一部分,是否有计划将其包含在以后的版本中(例如,C++ 14或更高版本)?
  • 如果有人可以进一步详细说明使用这些技术中的任何一种(即,宏观防护对比#pragma once)的优点/缺点,那也是很好的.

Jam*_*nze 103

#pragma once不是标准.它是一种广泛的(但不是通用的)扩展,可以使用

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

它被认为是标准化,但被拒绝,因为它无法可靠地实施.(当您通过多个不同的远程安装程序访问文件时会出现问题.)

确保在单个开发中不存在包含保护冲突是相当容易的.对于可能被许多不同开发项目使用的库,显而易见的解决方案是在创建包含防护时生成大量随机字符.(每当你打开一个新标题时,都可以设置一个好的编辑器为你做这个.)即使没有这个,我还没有遇到任何库之间冲突的问题.

  • 为什么编译器不能使用SHA-1或MD5校验和来识别文件? (43认同)
  • 如果您的代码通过符号链接或奇怪的挂载包含来自不同位置的某些文件,则它已经不可移植.因此,争论"pragma once"无法移植实现本质上不可移植(甚至不应该被考虑)的东西,这是C++颠倒世界的又一个废话. (35认同)
  • 如果每个主要的编译器都支持它,我真的没有意识到没有在标准中添加某些内容.标准中实际上有些东西远不如此支持.此外,当我们讨论包含文件时,抱怨边缘问题似乎很愚蠢,文件名冲突已经成为一个巨大的问题.如果对100%无问题功能的需求一般应用于#included头文件的概念,那就太好了. (23认同)
  • 不只是远程坐骑.硬链接,软链接,替代构造(在Windows上).它可能变得非常混乱. (11认同)
  • @JoseAntonioDuraOlmos我同意符号链接是一个OS功能,它超出了C++语言的范围.因此,问题出现了为什么C++ comitee应该考虑超出语言范围的东西?试图保证不是他们的责任,这对IMO没有任何意义.DOS每个文件名只支持8 + 3个字符,但没有人认为必须删除"#include",因为人们可以盲目地滥用该指令.`#pragma once`不会以任何方式限制可移植性,前提是您不会利用符号链接来破坏编译. (7认同)
  • @doc C++包括没有符号链接或已安装文件的概念.它们只是指定一个路径/文件名,由编译器-OS组合决定如何找到该文件.对于某些操作系统和文件系统,可以保证文件身份,而不是其他操作系统.这是com ++承认的C++语言之外的限制,因为它希望C++可以广泛使用,所以通过不使用`#pragma once`来解决它.此外,标题保护是一个已经解决的问题,不需要另外的解决方案,这进一步扩大了语言和实现. (5认同)
  • @Tonny有很多东西可以混淆编译器.但是,通常有办法解决这些问题.例如,在Unix下,编译器可以使用`stat`,并查看inode编号.如果inode编号不同,则文件不同.如果inode编号相同,_和_文件在同一文件系统上,则文件是相同的.硬链接必须位于同一文件系统上,并且可以解析软链接.但远程挂载可能导致本地出现的同一文件位于两个不同的文件系统上. (4认同)
  • @AnixPasBesoin SHA-1的冲突极不可能. (4认同)
  • 我认为,一旦你开始对每个包含文件进行加密,任何加速编译时间的声明就会消失. (4认同)
  • @Mark VY,我打赌哈希是比标记化更快的订单. (4认同)
  • 请扩展此答案以解释不在本地磁盘上的文件是如何重要的. (3认同)

Sho*_*hoe 32

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

表单的预处理指令

# pragma pp-tokensopt new-line
Run Code Online (Sandbox Code Playgroud)

导致实现以实现定义的方式运行.该行为可能导致转换失败或导致转换程序或生成的程序以不符合的方式运行.将忽略实现无法识别的任何编译指示.

基本上#pragma once#pragma指令的实现特定实例,不,它不是标准的.然而.

它通常得到大多数"主要编译器"的广泛支持,包括GCCClang,因此有时建议避免使用包含防护装置.

  • _"忽略实现无法识别的任何编译指示"_.这是否意味着消息:_Warning:无法识别的pragma指令_是否符合要求? (18认同)
  • @Yakk:如果有人写`#define` header-guard,他/她也没有理由写`#pragma once`. (18认同)
  • 请注意,您可以同时使用`#pragma`和`#define` header-guard. (10认同)
  • "因此,这是避免包含防范样板的推荐方法" - 一个非常大胆的声明.这是一种非标准的方式,使用它的好处很少,而且在我的经验中几乎没有相关性,所以我不得不把我的+1拿走. (5认同)
  • @Nawaz编译器可以保存每个文件的缓存(通过路径),这个文件是`#pragma once`d,如果它是`#include`d再次可以跳过`#include`(甚至不打开文件).[gcc做同样的](https://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html)有标题保护,但它非常非常脆弱.`#pragma`一个很容易做,头卫一个很难. (5认同)
  • @rodrigo我不明白为什么那应该是不符合的,它仍然可以忽略它并警告万一你拼写错误. (4认同)
  • @Yakk:怎么样*你还没有考虑的另一个编译器(也许是因为它尚未编写)?* - `#pragma once`不是标准的,这是**使用它的原因**.包含警卫是标准的,是否最有效率不是**脆弱**(它可能是*慢*,但不是*脆弱*).gcc文档中的规则非常清楚:如果在防护之外没有非空行,我将进行优化(这是在删除注释后完成的).当你说**脆弱**时听起来像*可能会破坏*,但如果是这种情况你指向的方向错误 (4认同)
  • @DavidRodríguez一个不被理解的`#pragma`必须被忽略:一个新的编译器可以为`#pragma once`创造一个不同的含义,但不太可能.我所指的脆弱性是优化的脆弱性,而不是包含保护机制.因此,我的初始点,你可以**同时使用**,并且使用两者的一个原因是`#pragma once`优化是简单而平凡且得到广泛支持的,而`#define`后卫优化并非易事,是脆弱的,但保证产生你想要的行为(速度之外).因此,建议使用两者. (3认同)
  • @supercat:委员会的一名成员告诉我,在仔细讨论后,有一个协议,空行**是一个有效的诊断.打印空行并且生成结果的编译器是*兼容的*编译器.祝你卖给任何顾客好运. (3认同)
  • 好吧,我们确实有一些冲突问题包括警卫名称,所以我不会说`#pragma`没有优势...... ATL(IIRC)通过将GUID放入头部防护来解决这个问题,但它们很难看. (2认同)
  • @DavidRodriguez你知道clang是否支持它吗?图案一样吗?英特尔?MSVC?您尚未考虑的另一个编译器(可能是因为尚未编写)?每个编译器都支持`#pragma once`(并且标准被那些不理解它的人忽略),并且与gcc 的方法相比,优化(不再加载文件)简单而微不足道。模式的变体会在 gcc 中工作吗?最好检查gcc的解决方案并对其进行审核。如果你遵循公认的模式,gcc 会工作,但如果你不这样做呢?顺便说一句,`#pragma once\n#ifdef FOO\n#endif` 失败 (2认同)
  • @rodrigo:标准对诊断消息的唯一限制是(1)需要某些构造来生成至少一个诊断信息,以及(2)从有效程序生成诊断信息不得使系统过载,以致编译不能成功或无法产生所需的诊断。根据我对标准的阅读,编译器*总是*输出“警告:此编译器的诊断工具无用”作为其唯一的诊断将符合标准的字面意思,尽管可能不是意图。 (2认同)