我已经读过使用#pragma once时会有一些编译器优化,这会导致更快的编译。 我认为这是非标准的,因此可能造成跨平台兼容性问题。
这是非Windows平台(gcc)上大多数现代编译器支持的东西吗?
我想避免平台编译问题,但也想避免后备警卫的额外工作:
1 2 3 4 5 6 7
| #pragma once
#ifndef HEADER_H
#define HEADER_H
...
#endif // HEADER_H |
我应该担心吗? 我是否应该在这方面进一步消耗精力?
-
在问了一个类似的问题之后,我发现#pragma once似乎避免了VS 2008中的一些类视图问题。我正在逐步删除包含警卫并用#pragma once替换它们。
#pragma once确实有一个缺点(除非是非标准的),如果你在不同的位置有相同的文件(我们有这个因为我们的构建系统复制文件),那么编译器会认为这些是不同的文件。
-
但是你也可以在不同的位置有两个同名的文件,而不必费心去创建不同的#define NAMES,这是以HEADERFILENAME_H的形式出现的
-
你也可以拥有两个或多个带有相同#define WHATEVER的文件,这会导致无趣的结束,这就是我赞成使用pragma一次的原因。
-
没有说服力......将构建系统更改为不复制文件但使用符号链接的系统,或仅在每个翻译单元中的一个位置包含相同的文件。听起来更像是你的基础设施是一个必须重组的混乱。
-
哇,我浪费了几天时间。得出了这个结论,很高兴看到它得到证实。
-
如果在不同的目录中有不同的同名文件,#ifdef方法会认为它们是同一个文件。因此,存在一个缺点,另一个存在缺点。
-
@rxantos,如果文件不同,#ifdef宏值也可能不同。
-
这不是#ifndef include-guard方法的缺点吗?在我看来,包括一个给定的标题两次通常是一个更安全的情况(例如,没有坏事可能发生),而不是包括两个不同的标题之一(可能会停止工作)。
-
@stevenlu实际发生的事情有时是设置项目,使标题自动复制到公共文件夹并标记为只读。在这种情况下,它们是相同的文件,与多个符号相关的错误可能会浪费大量时间。从经验谈起:)
-
确保构建设置总是在一个几乎任何明显成熟的项目中复杂化:)希望很快就能通过模块进入一个更加开明的时代
-
@rxantos大多数都不是问题。我自己包含的许多项目都使用SUBSYSTEM_HEADERNAME_H,而不仅仅是HEADERNAME_H(例如,COMMON_CONTAINERS_GRID_H或ENGINE_LOGIC_GAMESTATE_H(在我的多二进制项目中都遵循Binary_Subsystem_File))。在同一子系统中具有两个相同的文件名的可能性要小得多,如果确实发生了,那么很可能有人写了多余的东西,而你想知道它。
-
我应该说,"有些项目",而不是"很多",因为我真的不确定有多少使用这种方法,并且没有必要夸大使用方法。 = P
-
我认为这是有利的,因为一个人可以在不同的命名空间中拥有相同的文件(每个都有自己的目录)并且它可以工作。
使用#pragma once应该适用于任何现代编译器,但我认为没有任何理由不使用标准的#ifndef include guard。它工作得很好。需要注意的是,GCC在版本3.4之前不支持#pragma once。
我还发现,至少在GCC上,它识别标准#ifndef包含保护并对其进行优化,因此它不应该比#pragma once慢得多。
-
它根本不应该慢(无论如何都是GCC)。
-
我没有做过任何测试,所以我不确定那里有多大差异。我猜#ifndef可能比#pragma稍慢一些,因为它必须扫描到文件的末尾以确保有#endif。
-
它不是那样实现的。相反,如果文件第一次以#ifndef开头并以#endif结尾,gcc会记住它并始终跳过包含在未来的内容,甚至无需打开文件。
-
是的,它并不比#pragma once慢得多。它是完全相同的。唯一的好处是避免使用stat系统调用,这在C ++编译期间肯定可以加起来。
-
#pragma once通常更快,因为文件未经过预处理。 ifndef/define/endif无论如何都需要预处理,因为在这个块之后你可以有一些可编译的东西(理论上)
-
GCC关于守卫宏优化的文档:gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html
-
要使用包含保护,还需要定义一个新符号,例如#ifndef FOO_BAR_H,通常用于诸如"foo_bar.h"之类的文件。如果您稍后重命名此文件,是否应该相应地调整包含保护以符合此约定?此外,如果在代码树中的两个不同位置有两个不同的foo_bar.h,则必须考虑每个符号的两个不同符号。简短的回答是使用#pragma once,如果你真的需要在不支持它的环境中编译,那么继续为该环境添加包含保护。
-
名称冲突怎么样?
-
Solaris编译器还实现了保护宏优化:"许多编译器,包括Sun Studio 11,将识别典型的包含保护,并且永远不会再次打开文件。"
-
@Andrey:智能编译器会将文件标记为不必再次处理(除非定义的预处理器符号已被取消)。
-
使用#ifndef的旧Guard引发了#pragma一次不存在的问题,我们还必须输入匹配的#endif。如果我们在同一个头文件中也使用其他#ifdef,这可能会导致一些麻烦。如果忘记了任何#endif,错误将出现在错误的文件中,并且可能非常烦人。另一个麻烦是如果开发人员错误地为两个文件键入相同的防护(当然不可能发生开发者是完美的)。在某些设置中,这个甚至可以在一段时间内无人值守(如果没有编译单元使用两个头文件)。 #pragma曾经没有这样的麻烦
-
请注意:如果您使用的是像VS或Eclipse这样的IDE,则编译器不是唯一需要担心的问题。我发现#define警卫可能会过多地填充列表,这些在非常大的项目中搜索符号时会有点烦人。
-
没有#pragma曾经发源于微软?
-
如果您有两个具有相同名称的文件,#ifndef include guard将失败,这通常发生在库中。如果库A具有"array.h"而B具有"array.h",则两者都可能具有ARRAY_H作为包含保护,因此其中一个将被意外排除。
我希望#pragma once(或类似的东西)符合标准。包括警卫不是一个真正的大问题(但他们似乎有点难以向学习该语言的人解释),但这似乎是一个可以避免的轻微烦恼。
事实上,由于99.98%的时间,#pragma once行为是理想的行为,如果编译器自动处理阻止多个包含头的话会很好,使用#pragma或允许双重包含的内容。
但是我们拥有的东西(除了你可能没有#pragma once)。
-
我真正想要的是标准的#import指令。
-
标准的进口指令即将发布:isocpp.org/blog/2012/11/但尚未到此。我强烈支持它。
-
@AHelps Vaporware。已经差不多五年了。也许在2023年你会回到这个评论并说"我告诉你了"。
-
它不是蒸发器,但它只是在技术规范阶段。模块在Visual Studio 2015(blogs.msdn.microsoft.com/vcblog/2015/12/03/)和clang(clang.llvm.org/docs/Modules.html)中实现。它是导入的,而不是#import。
-
应该把它变成C ++ 20。
我不知道任何性能优势,但它确实有效。我在所有的C ++项目中使用它(授予我使用MS编译器)。我发现它比使用更有效
1 2 3 4
| #ifndef HEADERNAME_H
#define HEADERNAME_H
...
#endif |
它执行相同的工作,并且不会使用其他宏填充预处理器。
从版本3.4开始,GCC正式支持#pragma once。
自3.4以来,GCC支持#pragma once,请参阅http://en.wikipedia.org/wiki/Pragma_once以获得进一步的编译器支持。
我在使用#pragma once而不是包含警卫时看到的巨大好处是避免复制/粘贴错误。
让我们面对现实:我们大多数人几乎不能从头开始新的头文件,而只是复制现有的头文件并根据我们的需要进行修改。使用#pragma once而不是包含保护来创建工作模板要容易得多。我修改模板的次数越少,我就越不容易遇到错误。在不同的文件中包含相同的防护导致奇怪的编译器错误,并且需要一些时间来弄清楚出了什么问题。
TL; DR:#pragma once更易于使用。
我使用它并且我很满意它,因为我必须输入更少的东西来制作新的标题。它在三个平台上运行良好:Windows,Mac和Linux。
我没有任何性能信息,但我相信#pragma和include guard之间的区别与解析C ++语法的速度相比毫无结果。这是真正的问题。例如,尝试使用C#编译器编译相同数量的文件和行,以查看差异。
最后,使用后卫或者pragma,根本不重要。
-
我不喜欢#pragma一次,但我很感激你指出相对的好处......在"正常"的操作环境中,C ++解析比其他任何东西都要昂贵得多。如果编译时是个问题,那么没有人从远程文件系统编译。
-
Re C ++解析慢与C#相比。在C#中,您不必为每个微小的C ++文件解析(字面上)成千上万个头文件(iostream,任何人?)。但是,使用预编译的标头可以减小此问题
使用'#pragma once'可能没有任何影响(它在任何地方都不受支持 - 尽管它得到越来越广泛的支持),所以你需要使用条件编译代码,在这种情况下,为什么要打扰'#pragma once'?无论如何,编译器可能会优化它。但它确实依赖于您的目标平台。如果你的所有目标都支持它,那么继续使用它 - 但它应该是一个有意识的决定,因为如果你只使用pragma然后移植到不支持它的编译器,那么所有地狱都会破裂。
-
我不同意你必须支持警卫。如果你使用#pragma once(或guards),这是因为它会在没有它们的情况下引发一些冲突。因此,如果您的链工具不支持该项目,则该项目将无法编译,并且您完全处于与在旧的K&R编译器上编译某些ansi C时相同的情况。您只需获得更新的chaintool或更改代码以添加一些警卫。如果程序正在编译但无法工作,那么一切都会破坏。
性能优势在于,一旦读取了#pragma,就不必重新打开文件。使用警卫,编译器必须打开文件(时间成本很高)才能获得不应再包含内容的信息。
这是理论,因为对于每个编译单元,一些编译器将自动不打开没有任何读取代码的文件。
无论如何,对于所有编译器来说都不是这样,所以理想情况下,跨平台代码必须避免使用#pragma,因为它根本不是标准的/没有标准化的定义和效果。然而,实际上,它确实比守卫更好。
最后,你可以确保从编译器获得最佳速度而不必在这种情况下检查每个编译器的行为,这是一个更好的建议,就是使用pragma once和guard。
1 2 3 4 5 6 7 8 9 10 11 12
| #ifndef NR_TEST_H
#define NR_TEST_H
#pragma once
#include"Thing.h"
namespace MyApp
{
// ...
}
#endif |
这样你就可以获得最佳效果(跨平台和帮助编译速度)。
由于打字时间较长,我个人使用一种工具来帮助以一种非常邪恶的方式生成所有这些(Visual Assist X)。
-
Visual Studio不会按原样优化#include防护吗?其他(更好?)编译器这样做,我想它很容易。
-
你为什么把pragma放在ifndef之后?有好处吗?
-
@ user1095108有些编译器会使用标题保护作为分隔符来了解文件是否只包含必须实例化一次的代码。如果某些代码在标头保护之外,那么整个文件可能被认为可能多次实例化。如果同一个编译器不支持pragma一次,那么它将忽略该指令。因此,将pragma一次放入标头防护装置是最通用的方法,以确保至少可以"优化"标头防护。
不总是。
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52566有两个文件的好例子,两个文件都包含在内,但由于时间戳和内容相同(文件名不同),误认为是相同的。
-
那将是编译器中的一个错误。 (试图采取不应该采取的捷径)。
-
#pragma once是非标准的,因此无论编译器决定做什么都是"正确的"。当然,那么我们可以开始谈论什么是"预期的"和什么是"有用的"。
主要区别在于编译器必须打开头文件才能读取包含保护。相比之下,pragma曾经导致编译器跟踪文件,并且当遇到同一文件的另一个包含时不执行任何文件IO。虽然这听起来可以忽略不计,但它可以轻松扩展到大型项目,特别是那些没有良好标题的项目包括学科。
也就是说,这些天编译器(包括海湾合作委员会)足够聪明,可以像pragma一样对待包括守卫。即他们不打开文件并避免文件IO惩罚。
在不支持pragma的编译器中,我看到手动实现有点麻烦。
1 2 3
| #ifdef FOO_H
#include"foo.h"
#endif |
我个人喜欢#pragma曾经的方法,因为它避免了命名冲突和潜在拼写错误的麻烦。相比之下,它也是更优雅的代码。也就是说,对于可移植代码,除非编译器抱怨它,否则不应该受到影响。
-
"那就是说,这些天编译器(包括海湾合作委员会)都足够聪明,可以像对待pragma这样的警卫一次。"他们已经做了几十年,可能比#pragma once已经存在了!
-
认为你误解了我。我想在pragma之前说过,所有编译器都会在预处理器阶段多次包含多个IO文件。现代实现最终使用预处理器阶段中更好的文件缓存。无论如何,如果没有编译指示,预处理器阶段最终仍然包括包含警卫之外的所有内容。使用pragma一次,整个文件都被遗漏了。从这个角度来看,pragma仍然是有利的。
-
不,这是错误的,正确的编译器即使没有#pragma一次也会将整个文件丢弃,他们不会再次打开文件,他们甚至不会再次查看它,请参阅gcc.gnu.org/onlinedocs/ cpp / Once-Only-Headers.html(这与缓存无关)。
-
从您的链接,似乎优化只发生在cpp。无论如何,缓存确实发挥了作用。预处理器如何知道在守卫之外包含代码。示例... extern int foo; #ifndef INC_GUARD #define INC_GUARD class ClassInHeader {}; #endif在这种情况下,预处理器必须包含extern int foo;如果多次包含同一文件,则多次。一天结束时,只要我们理解#pragma曾经和包含警卫之间的区别以及各种编译器如何与他们两者表现出来,就没有多少争论过这个问题:)
-
显然,它不适用于优化。
今天老派包括守卫和#pragma一样快。即使编译器没有特别处理它们,它仍然会在看到#ifndef WHATEVER并定义WHATEVER时停止。今天打开文件很便宜。即使有改进,它也会达到毫秒级。
我只是不使用#pragma一次,因为它没有任何好处。为避免与其他包含警卫发生冲突,我使用如下内容:CI_APP_MODULE_FILE_H - > CI =公司缩写; APP =申请名称;其余的是不言自明的。
-
打字的好处不是很少吗?
-
请注意,几十分钟的几毫秒是几分钟。大型项目包含数万个文件,每个文件包含数十个标题。鉴于目前的多核CPU,输入/输出,特别是打开许多小文件,是主要的瓶颈之一。
-
"今天的老派包括警卫和#pragma一样快。"今天,也许多年前。海湾合作委员会网站上最古老的文件是2001年的2.95,优化包括警卫并不是新的。这不是最近的优化。
-
主要好处是包括警卫容易出错和冗长。在不同的目录中拥有两个具有相同名称的不同文件(并且包含保护可能是相同的符号),或者在复制包括保护时发生复制粘贴错误,这太容易了。 Pragma曾经不易出错,适用于所有主要的PC平台。如果你可以使用它,它是更好的风格。
在非常大的树上使用gcc 3.4和4.1(有时使用distcc),当使用#pragma代替或与标准包含警卫结合使用时,我还没有看到任何加速。
我真的不知道它的价值如何可能混淆旧版本的gcc,甚至其他编译器,因为没有真正的节省。我没有尝试过各种各样的指针,但我愿意打赌它会让很多人感到困惑。
我也希望它早日被采用,但我可以看到"当ifndef工作得很好时,为什么我们需要它?"。考虑到C的许多黑暗角落和复杂性,包括守卫是最简单,自我解释的事情之一。如果您对预处理器的工作原理知之甚少,那么它们应该是自我解释的。
但是,如果您确实观察到了显着的加速,请更新您的问题。
人们认为总是需要自动一次性包含头文件的附加说明:我使用了几十年的头文件的双重或多重构建代码生成器。 特别是对于协议库存根的生成,我发现拥有一个非常便携且功能强大的代码生成器而没有其他工具和语言非常舒适。 我不是唯一使用此方案的开发人员,因为这个博客是X-Macros节目。 如果没有丢失的自动防护,这是不可能做到的。
-
C ++模板能解决问题吗?由于C ++模板的原因,我很少发现对宏的任何有效需求。
-
我们的长期专业经验是,始终使用成熟的语言,软件和工具基础架构使我们作为服务提供商(嵌入式系统)在生产力和灵活性方面具有巨大优势。开发基于C ++的嵌入式系统软件和堆栈的竞争对手可能会发现他们的一些开发人员在工作中更开心。但我们通常会多次超越它们的上市时间,功能和灵活性。虚空低估了一次又一次使用同一个工具所带来的生产力提升。 Web-Devs确实遭遇了许多框架的方法。
如果我们使用msvc或Qt(最高Qt 4.5),由于GCC(最多3.4),msvc都支持#pragma once,我看不出没有使用#pragma once的理由。
源文件名通常是相同的类名,而且我们知道,有时我们需要重构,重命名类名,那么我们也必须更改#include XXXX,所以我认为手动维护#include xxxxx不是一个聪明的工作。即使使用Visual Assist X扩展,维护"xxxx"也不是必要的工作。