条件包含
预处理器支持源文件部分的条件编译。此行为由以下指令控制:
语法
1 | #if | 表达式 | ||
2 | #ifdef | 标识符 | ||
3 | #ifndef | 标识符 | ||
4 | #elif | 表达式 | ||
5 | #elifdef | 标识符 | (自 C++23 起) | |
6 | #elifndef | 标识符 | (自 C++23 起) | |
7 | #else | |||
8 | #endif |
解释
-
条件预处理块以 #if、#defif 或 #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、#defif 和 #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 identifier 或 defined (identifier) 的一元运算符。如果 *identifier* 被 定义为宏名,则结果为
1
,否则结果为0
。__has_include
和__has_cpp_attribute
(自 C++20 起) 在此上下文中被视为已定义的宏名。 (自 C++17 起) - __has_include 表达式,用于检测头文件或源文件是否存在 (自 C++17 起).
- __has_cpp_attribute 表达式,用于检测给定的属性标记是否受支持及其支持的版本。 (自 C++20 起)
在所有宏展开和 defined、__has_include
(自 C++17 起) 和 __has_cpp_attribute
(自 C++20 起) 表达式求值后,任何不是 布尔字面量 的标识符都将被替换为数字 0
(这包括词法上是关键字的标识符,但不是替代标记如 and
)。
然后,表达式将作为 整数常量表达式 进行求值。
如果 *expression* 求值为非零值,则包含受控代码块,否则跳过。
注意:在解决 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
。#elifdef identifier
基本上等同于#elif defined identifier
。 (自 C++23 起)#elifndef identifier
基本上等同于#elif !defined identifier
。 (自 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
// Note that if a compiler does not support C++23's #elifdef/#elifndef
// directives then the "unexpected" block (see below) will be selected.
#ifdef CPU
std::cout << "4: no1\n";
#elifdef GPU
std::cout << "4: no2\n";
#elifndef RAM
std::cout << "4: yes\n"; // expected block
#else
std::cout << "4: no!\n"; // unexpectedly selects this block by skipping
// unknown directives and "jumping" directly
// from "#ifdef CPU" to this "#else" block
#endif
// To fix the problem above we may conditionally define the
// macro ELIFDEF_SUPPORTED only if the C++23 directives
// #elifdef/#elifndef are supported.
#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"; // expected block
#else
std::cout << "4: no3\n";
#endif
#else // when #elifdef unsupported use old verbose `#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"; // expected block
#else
std::cout << "4: no3\n";
#endif
#endif
}
1: yes
2: yes
3: yes
4: no!
4: yes
缺陷报告
以下改变行为的缺陷报告已追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 发布时的行为 | 正确行为 |
---|---|---|---|
CWG 1955 | (C++98) | 失败的 #elif 的表达式被要求是合法的 | 失败的 #elif 被跳过 |