条件包含
预处理器支持对源文件的部分进行条件编译。此行为由 #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。__has_include
和__has_cpp_attribute
(自 C++20) 在此上下文中被视为已定义的宏的名称。(自 C++17)- (自 C++17) __has_include 表达式,它检测头文件或源文件是否存在。
- (自 C++20) __has_cpp_attribute 表达式,它检测是否支持给定的属性令牌及其支持的版本。
在所有宏展开和 defined
、 __has_include
(自 C++17) 和 __has_cpp_attribute
(自 C++20) 表达式的求值之后,任何不是 布尔字面量 的标识符都将替换为数字 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 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 被跳过 |
[编辑] 参见
C 文档 关于 条件包含
|