Conditional inclusion
预处理器支持对源文件部分内容进行条件编译。该行为由
#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
。
|
表达式 还可以包含以下表达式:
在上述语境中,提及的标识符会被视为已定义宏的名称。 |
(since C++17) |
在所有宏展开和计算
defined
及上述表达式后,任何不是
布尔字面量
的标识符都会被替换为数字
0
(这包括词法上属于关键字的标识符,但不包括替代记号如
and
)。
随后该表达式将作为 整型常量表达式 进行计算。
如果 表达式 求值为非零值,则包含受控代码块,否则跳过。
注意:在
CWG issue 1955
解决之前,
#if
cond1
...
#elif
cond2
与
#if
cond1
...
#else
后接
#if
cond2
有所不同。因为如果
cond1
为真,则第二个
#if
会被跳过,此时
cond2
不需要是合法表达式;而
#elif
中的
cond2
必须是合法表达式。根据 CWG 1955 的决议,导致代码块被跳过的
#elif
现在也会被跳过。
组合指令
检查该标识符是否 被定义为宏名称 。
#ifdef
identifier
本质上等同于
#if defined
identifier
。
#ifndef
identifier
本质上等同于
#if !defined
identifier
。
|
|
(自 C++23 起) |
注释
虽然
#elifdef
和
#elifndef
指令的目标是 C++23,但鼓励实现将它们作为符合规范的扩展向后移植到较旧的语言模式。
示例
#define ABCD 2 #include <iostream> int main() { #ifdef ABCD std::cout << "1: yes\n"; #else std::cout << "1: no\n"; #endif #ifndef ABCD std::cout << "2: no1\n"; #elif ABCD == 2 std::cout << "2: yes\n"; #else std::cout << "2: no2\n"; #endif #if !defined(DCBA) && (ABCD < 2*4-3) std::cout << "3: yes\n"; #endif // 注意:如果编译器不支持 C++23 的 #elifdef/#elifndef 指令 // 则会选择“意外”的代码块(见下文) #ifdef CPU std::cout << "4: no1\n"; #elifdef GPU std::cout << "4: no2\n"; #elifndef RAM std::cout << "4: yes\n"; // 预期代码块 #else std::cout << "4: no!\n"; // 意外地选择此代码块,因为跳过了 // 未知指令,直接从 "#ifdef CPU" // 跳转至此 "#else" 代码块 #endif // 为解决上述问题,我们可以有条件地定义 // 宏 ELIFDEF_SUPPORTED,仅当支持 C++23 指令 // #elifdef/#elifndef 时 #if 0 #elifndef UNDEFINED_MACRO #define ELIFDEF_SUPPORTED #else #endif #ifdef ELIFDEF_SUPPORTED #ifdef CPU std::cout << "4: no1\n"; #elifdef GPU std::cout << "4: no2\n"; #elifndef RAM std::cout << "4: yes\n"; // 预期代码块 #else std::cout << "4: no3\n"; #endif #else // 当不支持 #elifdef 时,使用旧的冗长写法 "#elif defined" #ifdef CPU std::cout << "4: no1\n"; #elif defined GPU std::cout << "4: no2\n"; #elif !defined RAM std::cout << "4: yes\n"; // 预期代码块 #else std::cout << "4: no3\n"; #endif #endif }
可能的输出:
1: yes 2: yes 3: yes 4: no! 4: yes
缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的C++标准。
| 缺陷报告 | 适用标准 | 发布时行为 | 正确行为 |
|---|---|---|---|
| CWG 1955 | C++98 | 失败的 #elif 表达式必须有效 | 失败的 #elif 应被跳过 |
参见
|
C 文档
关于
条件包含
|