条件包含
预处理器支持源文件部分的条件编译。此行为由 #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 issue 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++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
CWG 1955 | C++98 | 失败的 #elif 的表达式被要求是有效的 | 失败的 #elif 被跳过 |
[编辑] 参阅
C 文档 关于 条件包含
|