#pragma once 和 include guards 有什么区别?

461
我正在处理一个已知只能在Windows上运行并在Visual Studio下编译的代码库(它与Excel紧密集成,因此不会迁移)。我想知道是否应该使用传统的包含保护或者使用#pragma once来编写我们的代码。我认为让编译器处理#pragma once将会产生更快的编译速度,并且在复制和粘贴时也不容易出错。而且稍微好看一点;)

注意:为了获得更快的编译时间,我们可以使用Redundant Include Guards,但这会在被包含文件和包含文件之间添加紧密耦合。通常这没问题,因为保护应该基于文件名,并且只有在需要更改包含名称时才会更改。

13个回答

422

我不认为这会显著影响编译时间,但是#pragma once在各个编译器中得到了很好的支持,但它实际上并不是标准的一部分。预处理器可能会更快,因为它更容易理解您的确切意图。

#pragma once更不容易出错,而且打代码也更少。

要加快编译时间,请尽可能使用前向声明而不是在.h文件中包含它们。

我喜欢使用#pragma once

请参阅关于同时使用两者的可能性的维基百科文章


36
根据维基百科,一些编译器会优化#pragma once,而一些编译器(例如gcc)也会优化include保护。我的直觉告诉我应该使用你的代码已经在用的系统。我同时使用#pragma once和保护。 - KitsuneYMG
10
@Donnie - 这是gcc预处理器应用于包含保护的优化。它会自动检测头文件中的包含保护,并且如果有一个包含保护,它会将该文件视为已经使用了#pragma once。如果再次包含该文件,并且在此期间包含保护未被取消定义,则不会重新打开或重新解析该文件。 - Steve Jessop
12
#pragma once 不可靠,因为它存在不同文件层次结构(当前工作目录)、软连接、硬链接、网络文件系统甚至名称冲突(尝试使用名为 string.h 的文件)等问题。如果不考虑速度,你可以编写一个脚本,将文件中的任何 %INCLUDE_GUARD% 替换为自动管理的保护符号;你可以将头文件命名为 header.hpp.in,因为你已经有了预处理条件对,最终的头文件不会流动,并且编译器会在诊断信息中正确地输出行号。 - djsp
54
我不理解所谓的 #pragma once 可能存在的可靠性问题。如果我有两个不同的头文件 foo/string.hbar/string.h,那么使用 #pragma once 意味着我可以将它们都包含一次,同时包含它们也没有问题。如果我使用 include guard,那么我可能会在两个文件中写类似于 #ifndef STRING_H 的内容,这意味着我不能同时包含 foo/string.hbar/string.h - Brandin
13
如果你有两个文件都命名为string.h,那么如果你没有使用命名空间,很可能会出现内部名称冲突,因为这两个文件都可能涉及字符串操作。如果你使用了命名空间,我认为你应该在包含保护标识符中包括命名空间。 - JAB
显示剩余13条评论

209

我想在这个讨论中补充一点,我只是在VS和GCC上进行编译,并使用了包含保护。现在我已经转而使用#pragma once,这样做的唯一原因不是性能、可移植性或标准,因为我并不关心它的标准,只要VS和GCC支持就可以了,原因如下:

#pragma once 减少了引入错误的可能性。

将一个头文件复制并粘贴到另一个头文件中,并修改以适应自己的需求,然后忘记更改包含保护的名称非常容易。一旦两者都被包含,查找错误需要花费一段时间,因为错误消息并不一定清晰。


7
这是正确的原因。忘记性能,我们应该使用 #pragma once,因为它更少出错。我想到了,如果编译器已经在追踪 include guards,那么他们已经完成了大部分必要的工作,当他们看到不同的文件使用相同的宏名称时发出警告。 - rieux
只是想告诉你,我今天亲身经历了这个问题。编译器可能不接受你创建的某种类型,你就坐在那里纳闷为什么编译器找不到这个类型,明明你已经正确地包含了它,并且还将文件添加到了构建脚本中(在我的情况下是CMake)。直到你意识到自己只是复制了头文件,却忘记修改包含保护,这时你才真正明白需要花一些时间才能解决这个问题。 - undefined

177

#pragma once存在无法修复的错误,不应该使用。

如果您的#include搜索路径非常复杂,编译器可能无法区分两个具有相同基本名称的头文件(例如a/foo.hb/foo.h),因此其中一个头文件中的#pragma once将会抑制两个。它也可能无法确定两个不同的相对包含(例如#include "foo.h"#include "../a/foo.h")是否指向同一文件,因此#pragma once将无法在应该抑制冗余包含时抑制它。

这也影响了编译器避免重新读取带有#ifndef守卫的文件的能力,但这只是一种优化。使用#ifndef守卫时,编译器可以安全地读取任何它不确定是否已经看过的文件;如果出现错误,它只需要做一些额外的工作。只要没有两个头文件定义相同的宏守卫,代码就会按预期编译。如果两个头文件确实定义了相同的宏守卫,程序员可以进去更改其中一个。
#pragma once没有这样的安全保障——如果编译器对头文件的身份认定错误,无论如何,程序都将无法编译。如果你遇到这个bug,你唯一的选择是停止使用#pragma once,或者重命名其中一个头文件。头文件的名称是API契约的一部分,因此重命名可能不是一个选项。

(为什么这是无法修复的问题的简短版本是,Unix和Windows文件系统API都没有提供任何机制来保证告诉你两个绝对路径名是否指向同一个文件。如果你认为inode号码可以用于此,抱歉,你错了。)

(历史注释:我之所以没有在12年前有权利这样做时将#pragma once#import从GCC中删除,唯一的原因是苹果公司的系统头文件依赖它们。回想起来,那不应该阻止我。)

(由于这在评论线程中已经出现了两次:GCC开发人员确实花了很多精力使#pragma once尽可能可靠;请参见GCC bug report 11569。然而,在当前版本的GCC中,实现仍然可能在合理的条件下失败,例如遭受时间偏差的构建农场。我不知道其他编译器的实现情况,但我不会期望有人做得更好。)


52
在技术层面上,我同意你的观点。但是如果这两个包含保护符有相同的名称,因为文件名相同(这很可能发生),那仍然无济于事。我理解正确吗? - Bim
62
FYI这个答案正在Reddit上被讨论。人们并不被你的论点所说服(我认为他们有一定道理)。 - Konrad Rudolph
25
“除非包含同一文件两次恰好是程序员想要的,否则不这样做将导致编译失败。”对我来说似乎不是个问题,因为这种情况下该文件不会有包含保护(include guard),除非在极少数的特殊情况下才会有。这些情况可以通过不对该文件使用#pragma once来解决。 - Jordan Melo
28
没问题,如果你这样做的话,就不应该有 #pragma once :) 要再次强调的是,必须对这个编译指示符进行一些明确的语义定义。如果你想多次包含同一内容,不要在其中加入 #pragma once - Kuba hasn't forgotten Monica
26
抱歉,如果A/foo.h和B/foo.h是相同的,并且有包含保护,则必然包含保护也是相同的,无论宏定义如何,在任何情况下第二个包含将仍然被忽略,除非这些定义在某种程度上改变了包含保护的含义(它们可以吗?)。据我所知,为了进行预处理器“计算”而需要多次包含的Boost源代码没有包含保护,如果它们有,那么就没有意义。因此,除非我对您的理解有误,否则我认为这不是一个真正的问题。 - Kuba hasn't forgotten Monica
显示剩余44条评论

45

#pragma once成为标准之前(当前未来标准的优先事项之一),建议您同时使用它和使用宏保护,这样:

#ifndef BLAH_H
#define BLAH_H
#pragma once

// ...

#endif
原因如下:
  • #pragma once 不是标准,所以有些编译器可能不提供该功能。尽管如此,所有主要的编译器都支持它。如果编译器不认识它,至少它会被忽略。
  • 由于 #pragma once 没有标准行为,您不应假设其行为在所有编译器上都相同。使用头文件保护可以确保所有实现了所需预处理器指令的编译器至少具有相同的基本假设。
  • 在大多数编译器上,#pragma once 可以加速编译(一个 cpp 文件),因为编译器不会重新打开包含该指令的文件。因此,将其放在文件中可能有助于加快编译速度,也可能不会,这取决于编译器。我听说 g++ 在检测到头文件保护时也可以进行相同的优化,但需要确认。

同时使用两者能够充分发挥它们各自的优点。

如果您没有一些自动脚本来生成头文件保护,那么使用 #pragma once 可能更方便。只需了解对于可移植代码意味着什么即可。(我正在使用 VAssistX 快速生成头文件保护和 #pragma once)

您几乎总是应该以可移植的方式考虑您的代码(因为您不知道未来是什么),但如果您确实认为它不适用于与另一个编译器编译(例如针对非常特定的嵌入式硬件的代码),那么您应该查看有关 #pragma once 的编译器文档,了解您正在做什么。


7
谁说#pragma once将被标准化?此外,有多少编译器不跟踪头文件是否完全包含在#include宏保护之中?换句话说,有人测量过#pragma once实际上是否会产生差异吗? - Richard Corden
4
@Richard,我已经做过了,在某些情况下确实如此——我们的项目包含了5500个文件,其中大约一半是多余的。 - Sam Harwell
9
认可它的编译器并不关心,但如果有包含保护之外的内容,则无法识别它的编译器可能会禁用包含保护优化。而没有进行优化的编译器会在重复包含时跳过保护内部的所有内容,因此对于内部的内容来说,所需处理的内容更少。 - Ben Voigt
9
看起来微软已经优化了他们的编译器。据微软VS2015文档所述:“在同一文件中同时使用#include保护习语和#pragma once没有任何优势。如果在标准形式的习语之前或之后没有非注释代码或预处理器指令,编译器将识别#include保护习语并实现多重包含优化,就像#pragma once指令一样。” https://msdn.microsoft.com/en-us/library/4141z1cx.aspx - cgmb
5
这里的“#pragma once”永远不会被正确地评估,因此是多余的,因为防护程序将始终覆盖它。不用担心,只需在较简单的项目中使用旧式防护程序或仅使用#pragma once即可。 - hookenz
显示剩余10条评论

43

从软件测试人员的角度来看

#pragma once 比include保护更短,更少出错,在大多数编译器中都被支持,并且一些人认为它编译得更快(这已经不再是真的了)。

但我仍建议您使用标准的#ifndef include保护。

为什么用#ifndef

考虑一个像这样的编造的类层次结构,其中每个类 ABC 都存在于各自的文件中:

a.h

#ifndef A_H
#define A_H

class A {
public:
  // some virtual functions
};

#endif
b.h
#ifndef B_H
#define B_H

#include "a.h"

class B : public A {
public:
  // some functions
};

#endif

c.h

#ifndef C_H
#define C_H

#include "b.h"

class C : public B {
public:
  // some functions
};

#endif

现在假设你正在编写类的测试,并且需要模拟非常复杂的类B的行为。一种方法是编写一个mock类,例如使用Google Mock,并将其放入目录mocks/b.h中。请注意,类名未更改,但仅存储在不同的目录中。但最重要的是include guard与原始文件b.h中的名称完全相同。

mocks/b.h

#ifndef B_H
#define B_H

#include "a.h"
#include "gmock/gmock.h"

class B : public A {
public:
  // some mocks functions
  MOCK_METHOD0(SomeMethod, void());
};

#endif

有什么好处?

采用这种方法,您可以模拟类B的行为,而无需触及原始类或告诉C。您所需要做的就是将目录mocks/放在编译器的包含路径中。

为什么不能使用#pragma once来实现?

如果您使用了#pragma once,您会遇到名称冲突问题,因为它不能保护您免于定义类B两次,一次是原始版本,一次是模拟版本。


9
这对于任何大型项目来说并不切实际。让B保持更新将会很麻烦,更不用说这违反了一个定义规则。 - parapura rajkumar
6
如果在包含 b.h 之前先包含 mocks/b.h,预处理器将完全跳过 b.h,从而只留下一个类 B,因此不违反 ODR。我想知道你对测试类 B 是否有更少“痛苦”的方法。你提出了什么策略来测试“任何大型项目”? - Konrad Kleine
3
没有人覆盖" gmock.h"。它是测试框架的一部分,我们希望用它模拟一个类。我认为我们在谈论两件不同的事情。整个重点是将现有的代码或类层次结构作为测试,将链中的一部分替换为模拟器或Mocking类。这样,您就可以有效地测试非测试代码。这不像单元测试,其中只测试一个类本身,但Mocking允许您进行系统测试。如果您的类层次结构使用经典的#ifndef包含保护,您可以在测试中仅用Mocking替换类。 - Konrad Kleine
4
如果原始的b.h和模拟的b.h都在编译器的包含路径中-那么#include "b.h"不会有名称冲突吗? 如果您从测试项目的包含目录中删除原始的b.h路径,以便只选择模拟的b.h,那么#pragma once也会起作用吗? - Samaursa
8
如果你将mocks/b.h加入到包含路径中,那么你应该删除原始的b.h,而不是依赖于包含保护。你的解决方案就像符号重定位一样令人失望。 - Navin
显示剩余3条评论

30

在讨论使用#pragma once#ifndef宏定义作为头文件保护方式的性能问题时,涉及到正确性的争议(我站在#pragma once这一面,基于一些比较近期的背景知识)。最终,我决定测试一下#pragma once是否更快,因为编译器不必重新尝试包含已经被包含过的文件。

为了测试,我自动生成了500个具有复杂相互依赖关系的头文件,并且有一个.c文件将它们全部#include。我进行了三次测试,一次只使用#ifndef,一次只使用#pragma once,一次同时使用两者。测试在一台相对较新的系统上运行(2014年款的MacBook Pro,运行OSX,使用XCode捆绑的Clang编译器,以及内置的SSD)。

首先,是测试代码:

#include <stdio.h>
 
//#define IFNDEF_GUARD
//#define PRAGMA_ONCE
 
int main(void)
{
    int i, j;
    FILE* fp;
 
    for (i = 0; i < 500; i++) {
        char fname[100];
 
        snprintf(fname, 100, "include%d.h", i);
        fp = fopen(fname, "w");
 
#ifdef IFNDEF_GUARD
        fprintf(fp, "#ifndef _INCLUDE%d_H\n#define _INCLUDE%d_H\n", i, i);
#endif
#ifdef PRAGMA_ONCE
        fprintf(fp, "#pragma once\n");
#endif
 
 
        for (j = 0; j < i; j++) {
            fprintf(fp, "#include \"include%d.h\"\n", j);
        }
 
        fprintf(fp, "int foo%d(void) { return %d; }\n", i, i);
 
#ifdef IFNDEF_GUARD
        fprintf(fp, "#endif\n");
#endif
 
        fclose(fp);
    }
 
    fp = fopen("main.c", "w");
    for (int i = 0; i < 100; i++) {
        fprintf(fp, "#include \"include%d.h\"\n", i);
    }
    fprintf(fp, "int main(void){int n;");
    for (int i = 0; i < 100; i++) {
        fprintf(fp, "n += foo%d();\n", i);
    }
    fprintf(fp, "return n;}");
    fclose(fp);
    return 0;
}

现在,是我的各种测试运行:

folio[~/Desktop/pragma] fluffy$ gcc pragma.c -DIFNDEF_GUARD
folio[~/Desktop/pragma] fluffy$ ./a.out 
folio[~/Desktop/pragma] fluffy$ time gcc -E main.c  > /dev/null

real    0m0.164s
user    0m0.105s
sys 0m0.041s
folio[~/Desktop/pragma] fluffy$ time gcc -E main.c  > /dev/null

real    0m0.140s
user    0m0.097s
sys 0m0.018s
folio[~/Desktop/pragma] fluffy$ time gcc -E main.c  > /dev/null

real    0m0.193s
user    0m0.143s
sys 0m0.024s
folio[~/Desktop/pragma] fluffy$ gcc pragma.c -DPRAGMA_ONCE
folio[~/Desktop/pragma] fluffy$ ./a.out 
folio[~/Desktop/pragma] fluffy$ time gcc -E main.c  > /dev/null

real    0m0.153s
user    0m0.101s
sys 0m0.031s
folio[~/Desktop/pragma] fluffy$ time gcc -E main.c  > /dev/null

real    0m0.170s
user    0m0.109s
sys 0m0.033s
folio[~/Desktop/pragma] fluffy$ time gcc -E main.c  > /dev/null

real    0m0.155s
user    0m0.105s
sys 0m0.027s
folio[~/Desktop/pragma] fluffy$ gcc pragma.c -DPRAGMA_ONCE -DIFNDEF_GUARD
folio[~/Desktop/pragma] fluffy$ ./a.out 
folio[~/Desktop/pragma] fluffy$ time gcc -E main.c  > /dev/null

real    0m0.153s
user    0m0.101s
sys 0m0.027s
folio[~/Desktop/pragma] fluffy$ time gcc -E main.c  > /dev/null

real    0m0.181s
user    0m0.133s
sys 0m0.020s
folio[~/Desktop/pragma] fluffy$ time gcc -E main.c  > /dev/null

real    0m0.167s
user    0m0.119s
sys 0m0.021s
folio[~/Desktop/pragma] fluffy$ gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/include/c++/4.2.1
Apple LLVM version 8.1.0 (clang-802.0.42)
Target: x86_64-apple-darwin17.0.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
正如您所看到的,带有“#pragma once”的版本确实比仅使用“#ifndef”时预处理速度稍快,但是差异非常小,在实际构建和链接代码所需的时间方面则会被远远掩盖。也许对于足够大的代码库来说,这可能会导致构建时间相差几秒钟,但由于现代编译器能够优化“#ifndef”保护,操作系统拥有良好的磁盘缓存,以及存储技术的不断提速,性能问题似乎已经不再重要,至少在当前典型的开发者系统中是这样。旧的和更奇特的构建环境(例如托管在网络共享中的头文件,从磁带构建等)可能会在某种程度上改变这个公式,但在这些情况下,似乎更有用的是首先建立一个不那么脆弱的构建环境。
事实是,“#ifndef”具有标准行为而“#pragma once”没有,并且“#ifndef”还处理了奇怪的文件系统和搜索路径边角情况,而“#pragma once”可能会因某些事物而混淆,从而导致程序员无法控制的不正确行为。使用“#ifndef”的主要问题在于程序员选择了糟糕的守卫名称(名称冲突等),即使如此,API的使用者仍然可以使用“#undef”覆盖这些不良名称——这可能不是一个完美的解决方案,但它是可能的,而如果编译器错误地剔除了一个“#include”,则“#pragma once”将没有任何补救措施。
因此,即使“#pragma once”在可证明的情况下(稍微)更快,我也不认为这本身就是使用它而不是使用“#ifndef”保护的理由。
增加头文件数量并将测试改为仅运行预处理步骤消除了编译和链接过程中添加的任何少量时间(之前很微不足道,现在不存在)。与预期的一样,差异大致相同。

"可忽略的"? 你将构建时间减半了。 - Lightness Races in Orbit
@LightnessRacesinOrbit 我将构建时间缩短了0.04秒。这恰好是一半,在一个源文件中,该文件是极其复杂的#include规则的病态表现,这在现实生活中可能永远不会出现。重点是测量#ifndef守卫对#pragma once的开销有多大,任何代码库如果超过0.04秒就存在更大的问题。 - fluffy
7
您的回答提供的上下文不足以判断在项目扩大时节省的时间是否仍将保持0.04秒的恒定值,或者是否将保持50%的恒定值,或者介于两者之间。因此,这并不是一个非常有用的基准,而且“可忽略”的结论目前没有得到支持。我只是提到这一点,因为我对真正的结论非常感兴趣! - Lightness Races in Orbit
2
这是一个很好的观点 - 它只测量了包含保护的速度,而不是预处理器的其余部分。我认为显而易见的是,生成的代码仅测试该方面,因为在保护线之间没有太多内容。也许我可以扩展生成的代码,使每个文件都有几千个随机生成的模板函数和类,这样就可以进行不同的不切实际的合成测试了。 - fluffy
1
不仅仅是预处理器 - 你正在进行完整的编译和链接。从这些结果中,我们无法知道“无关常数开销”是多少,因此结果实际上并没有告诉我们任何有用的信息。可悲! - Lightness Races in Orbit
1
@LightnessRacesinOrbit 我已经更改了测试,增加了更多的#include文件,并且只运行预处理器。时间差异毫不意外地大致相同(现在是一个更小的分数)。 - fluffy

28

如果您确定永远不会在不支持它的编译器(例如Windows / VS,GCC和Clang是支持它的编译器)中使用此代码,则可以放心地使用#pragma once。

您还可以同时使用两者(见下面的示例),以便在兼容系统上获得可移植性和编译速度加快。

#pragma once
#ifndef _HEADER_H_
#define _HEADER_H_

...

#endif

28
#pragma once 得到了许多不同编译器的很好支持,包括 GCC。 - Brian R. Bondy
6
没错,但并不是所有情况下都支持。预编译指令可以始终支持……这取决于你希望你的代码有多可移植。 - Donnie DeBoer
6
只要不影响可移植性,我真的不想两者都要,因为那会使代码变得难看。在我看来,代码的规范性问题是其次要的。 - Matt Price
7
即使您关心可移植性,也不必太在意它。如果您目前只针对Windows编写代码,则可能有其他内容将您的代码与Windows更紧密地绑定在一起,而不仅仅是#pragma once。如果某天您需要移植代码,编写一个Perl脚本来遍历头文件,并且用基于文件名和路径的包含保护替换所有使用#pragma once的代码并不困难。 - Steve Jessop
4
另一方面,如果项目的一部分是编写可能最终可在其他地方使用的代码片段(例如数学库、API包装器等),请考虑可移植性。你过于担心代码的外观而不是它的工作方式…尤其是文件顶部,人们已经学会忽略它了。 - Sean
显示剩余11条评论

15

这里有一个相关问题,我在另一个回答中已经回答过了:

#pragma once 的一个缺点(除了它不是标准的)是如果你在不同的位置拥有相同的文件(我们有这个情况,因为我们的构建系统会将文件复制到不同的位置),那么编译器将认为这些是不同的文件。

我在这里也添加了答案,以防有人看到这个问题而不是其他问题。


@ShitalShah 这只是一个考虑因素,如果你的构建系统复制文件。 - Motti
13
修复#pragma once的实现,它应该使用可靠的校验和而不是可疑的文件名。 - MickLH

15

通常我不使用 #pragma once,因为我的代码有时需要与除了MSVC或GCC之外的编译器编译(嵌入式系统的编译器并不总是支持 #pragma)。

所以我仍然需要使用 #include guards。一些答案建议我也可以使用 #pragma once,但似乎没有太多理由这样做,而且它会在那些不支持它的编译器上经常引起不必要的警告。

我不确定 #pragma 能带来多少时间节省。我听说编译器通常已经能识别出头文件中除了 guard 宏之外只有注释的情况,并在这种情况下执行 #pragma once 的等效操作(即,永远不再处理该文件)。但我不确定这是真的还是只是编译器“可能”进行的优化。

无论哪种情况,对我来说使用 #include guards 更容易,在任何地方都可以工作,也不必进一步担心它。


7
我很好奇哪个编译器不支持它。看起来大多数编译器都支持,我不知道有哪个不支持的。 - Shital Shah

9
我认为你应该做的第一件事就是检查一下这是否真的会产生影响,即首先测试性能。 在谷歌搜索中,其中一个结果是 this

在结果页面中,列对我来说稍微有点偏离,但很明显至少到VC6为止,Microsoft没有实现其他工具正在使用的包含保护优化。 对于内部包含保护,所需时间比外部包含保护长50倍(外部包含保护至少与#pragma一样好)。 但是让我们考虑这可能产生的影响:

根据所提供的表格,打开包含并进行检查所需的时间是#pragma等效方案的50倍。 但是,实际执行此操作所需的时间在1999年测量为每个文件1微秒!

那么,每个TU会有多少个重复的头文件?这取决于你的编码风格,但是如果我们假设一个平均的TU有100个副本,那么在1999年,我们每个TU可能要花费100微秒。随着HDD的改进,现在这个数字可能会显著降低,但即使如此,在使用预编译头文件和正确的依赖跟踪的情况下,对于项目来说,总累计成本几乎肯定不是构建时间的重要部分。

另一方面,尽管可能性很小,但如果您将来转移到不支持#pragma once的编译器,请考虑更新整个源代码库以使用包含保护符而不是#pragma需要多长时间?

没有理由Microsoft不能像GCC和其他编译器一样实现包含保护符优化(实际上有人能确认他们更近期的版本是否实现了这一点吗?)。我认为,#pragma once除了限制您选择替代编译器之外,几乎没什么用处。


7
不是挑剔,但如果你要将代码移植到不支持#pragma once的编译器上,编写一个可以扫描文件并用传统的包含保护替换#pragma once的工具可能相对于整个移植过程来说非常简单。 - Jherico
2
实际上,有人能否确认他们的更近版本是否实现了这个功能?在VS2015文档中提到,它现在确实实现了包含保护优化。https://msdn.microsoft.com/en-us/library/4141z1cx.aspx - cgmb
3
请考虑更新整个源代码库以使用include guards而不是#pragma需要多长时间?这不应该花费太长时间。为了使此过程更加容易,我编写了guardonce - cgmb

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