这个问题并没有一个完整的答案涵盖了大三编译器,所以这里是我尝试更完整的回答。
概括
- 简而言之:如果你关心可移植性,请将其放在可能与之冲突的任何
#include
或#define
语句之前(例如,将其放在头文件中的第一位)。
- 被所有主要编译器支持(“大三”x86_64编译器以及英特尔和嵌入式编译器)
- 位置通常不重要,只要它到达预处理器即可(例如,不被
#if
-分支阻塞)
- 不同的编译器对于是否应该首先使用它有不同的看法,但没有记录下未如此做会发生什么。
- 大多数编译器已经检测到包含保护并将其视为
pragma once
,使得好处主要是不创建唯一的保护名称。
以下是一个快速的摘要指南:
(1) - 支持,但受宏扩展的影响。
(2) - 支持,但文件头部应该放置该指令。
(3) - 支持,但不建议使用。
细节
GCC
来自GCC参考手册:
如果在扫描头文件时遇到#pragma once
,那么该文件将永远不会再次被读取,无论如何。这是防止头文件被多次包含的‘#ifndef
’的一种不太便携的替代方法。
(重点标注为我的)
扫描是在预处理期间进行的,只要#pragma
语句可见于预处理器(不在从#if
开始的不可达条件块中),则它将生效。
GCC的#pragma once
不受预处理器替换的影响。
在线示例
Clang
Clang的参考手册似乎并没有明确指定
#pragma once
,但据我所知,Clang应该与大多数(如果不是全部)GCC内置函数和特性兼容。查看Clang预处理阶段的源代码表明了你所期望的情况;它在预处理期间处理
#pragma once
。(
source)
void Preprocessor::HandlePragmaOnce(Token &OnceTok) {
...
HeaderInfo.MarkFileIncludeOnce(getCurrentFileLexer()->getFileEntry());
}
和GCC一样,#pragma once
的放置位置不重要,只要它是第一个,并且不受预处理器替换影响。
示例
MSVC
MSVC的#pragma once
文档没有说明它应该放在哪里,只是说它应该在源文件中(并提供了一个在顶部使用它的示例)。
正如其他人所提到的,当在MSVC中使用#pragma once
时,它会受到预处理器扩展的影响。
带有替换
示例
不带替换
示例
Intel (CL-Based)
在Windows上使用Intel编译器时,编译器会使用MSVC兼容模式(ICL)。虽然它没有在
支持的Pragma中记录,但似乎是被支持的。只要预处理器能够到达,放置位置似乎也不重要。
ICL的
#pragma once
存在与MSVC相同的预处理器扩展问题。
注意:icl
不受编译器探索器支持,因此没有示例可用。
Intel(基于GNU)
在Linux或旧版macOS版本(ICC)上使用英特尔编译器时,编译器使用GNU兼容模式。
与上面一样,它没有明确列为支持的编译指示, 但实际上似乎是被支持的。只要预处理器到达它,放置位置似乎也不重要。
ICC的#pragma once
存在预处理器扩展问题,这与MSVC的经验相同。
使用替换
在线示例
不使用替换
在线示例
英特尔(基于Clang)
新的英特尔ICX NextGen编译器基于Clang / LLVM技术。在行为上,与Clang相匹配。
与其他英特尔编译器不同,但与Clang一样,这不会遭受预处理器扩展问题。
实时示例
Arm(armcc
)
armcc
编译器建议不要使用#pragma once
, 但也提供了一个示例,它存在于#define
语句之后作为可选功能与包含保护一起使用。
根据示例,放置位置可能不是问题。
不清楚这是否会经历任何预处理器扩展。
注意: 在编译器浏览器上不支持armcc
,因此没有示例可用。
Texas Instruments(TI ArmCL)
如参考手册第5.11.23节所述:
This pragma should be used at the beginning of a header file that should only be included once. For example:
#pragma once
#warn You will only see this message one time
struct foo
{
int member;
};
(我的强调)
我没有测试过如果它被移动到注释头以下会发生什么,但编译器只 正式 支持在文件 开头 使用。
我认为这应该不重要,但无法确认。
不清楚这是否会经历任何预处理扩展。
注意: tiarmcl
(以及其他类似的ti编译器)不受编译器探索器支持,因此没有示例可用。
Texas Instruments(tiarmclang
)
这是clang
的一个分支,因此它的行为与clang
相同。
在此实现中,#pragma once
可以有效地放置在预处理器到达的任何位置,并且不涉及预处理器替换。
注意:编译器探索器不支持tiarmclang
,因此没有示例可用。