条件包含
预处理器支持有条件地编译源文件的某些部分。这一行为由 #if
、#else
、#elif
、#ifdef
、#ifndef
、#elifdef
、#elifndef
(C++23 起) 与 #endif
指令所控制。
目录 |
[编辑]语法
#if 表达式 | |||||||||
#ifdef 标识符 | |||||||||
#ifndef 标识符 | |||||||||
#elif 表达式 | |||||||||
#elifdef 标识符 | (C++23 起) | ||||||||
#elifndef 标识符 | (C++23 起) | ||||||||
#else | |||||||||
#endif | |||||||||
[编辑]解释
条件预处理块由 #if
、#ifdef
或 #ifndef
指令开始,然后可选地包含任意多个 #elif
、#elifdef
或 #elifndef
(C++23 起) 指令,接下来是最多一个可选的 #else
指令,并以 #endif
指令结束。嵌套的条件预处理块会被单独处理。
各个 #if
、#ifdef
、#ifndef
、#elif
、#elifdef
、#elifndef
(C++23 起) 和 #else
指令所控制的代码块在第一个不属于内部嵌套的条件预处理块的 #elif
、#elifdef
、#elifndef
(C++23 起)、#else
或 #endif
指令处结束。
#if
、#ifdef
和 #ifndef
指令测试其所指定的条件(见下文),如果条件求值为真,则编译其控制的代码块。此时后续的 #else
、#elifdef
、#elifndef
(C++23 起) 和 #elif
指令将被忽略。否则,如果所指定的条件求值为假,则跳过其所控制的代码块,然后处理后续的 #else
、#elifdef
、#elifndef
(C++23 起) 或 #elif
指令(如果存在)。若后续指令为 #else
,则 #else
指令所控制的代码块将会无条件地进行编译。否则,#elif
、#elifdef
或 #elifndef
(C++23 起) 指令按照与 #if
指令相同的方式执行:即测试条件是否满足,并根据其结果决定编译或跳过其所控制的代码块,并在后一种情况下继续处理后续的 #elif
、#elifdef
、#elifndef
(C++23 起) 和 #else
指令。条件预处理块以 #endif
指令结束。
[编辑]条件的求值
[编辑]#if, #elif
表达式 可以包含形如“defined
标识符”或“defined (
标识符)
”的一元运算符。当此标识符 已经被定义为宏名时结果是 1,否则结果是 0。
表达式 也可以包含以下表达式:
以上标识符在这种语境中被当做如同是已定义的宏名。 | (C++17 起) |
在进行所有宏展开和对 defined
以及上述表达式的求值后,任何非布尔字面量的标识符都被替换成数字 0(这包含词法上为关键字的标识符,但不包括如 and 之类的代用记号)。
然后,表达式被当作整数常量表达式进行求值。
当表达式 求值为非零值时,包含其所控制的代码块,否则跳过该代码块。
注:解决 CWG 问题 1955 前,“#if cond1
... #elif cond2
”和“#if cond1
... #else
”后面跟着“#if cond2
”是不同的,因为当 cond1
为真时第二个 #if
将被跳过,且 cond2
并不需要是良构的,而 #elif
中的 cond2
则必须是合法的表达式。 CWG 1955 开始,引领跳过的代码块的 #elif
也被跳过。
[编辑]组合指令
检查标识符是否被定义为宏名。
“#ifdef
标识符”与“#if defined
标识符”实质上等价。
“#ifndef
标识符”与“#if !defined
标识符”实质上等价。
“ “ | (C++23 起) |
[编辑]注解
#elifdef
与 #elifndef
指令在 C++23 标准化,不过鼓励实现将它们作为遵从的扩展向后移植到旧语言模式。
[编辑]示例
#define ABCD 2#include <iostream> int main(){ #ifdef ABCDstd::cout<<"1: yes\n";#elsestd::cout<<"1: no\n";#endif #ifndef ABCDstd::cout<<"2: no1\n";#elif ABCD == 2std::cout<<"2: yes\n";#elsestd::cout<<"2: no2\n";#endif #if !defined(DCBA) && (ABCD < 2*4-3)std::cout<<"3: yes\n";#endif // 注意若编译器不支持 C++23 的 #elifdef/#elifndef 指令则会选择“不期待”块(见后述)。#ifdef CPUstd::cout<<"4: no1\n";#elifdef GPUstd::cout<<"4: no2\n";#elifndef RAMstd::cout<<"4: yes\n";// 期待的块#elsestd::cout<<"4: no!\n";// 由于跳过未知的指令不期待地选择此块// 并直接从 "#ifdef CPU" “跳”到此 "#else" 块#endif // 为修复此问题,我们可以条件性地仅若支持 C++23 指令 #elifdef/#elifndef// 才定义 ELIFDEF_SUPPORTED 宏。#if 0#elifndef UNDEFINED_MACRO#define ELIFDEF_SUPPORTED#else#endif #ifdef ELIFDEF_SUPPORTED#ifdef CPUstd::cout<<"4: no1\n";#elifdef GPUstd::cout<<"4: no2\n";#elifndef RAMstd::cout<<"4: yes\n";// 期待的块#elsestd::cout<<"4: no3\n";#endif#else // 不支持 #elifdef 时使用累赘的旧 “#elif defined”#ifdef CPUstd::cout<<"4: no1\n";#elif defined GPUstd::cout<<"4: no2\n";#elif !defined RAMstd::cout<<"4: yes\n";// 期待的块#elsestd::cout<<"4: no3\n";#endif#endif}
可能的输出:
1: yes 2: yes 3: yes 4: no! 4: yes
[编辑]缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
CWG 1955 | C++14 | 要求失败的 #elif 中的表达式合法 | 跳过失败的 #elif |
[编辑]参阅
条件包含的 C 文档 |