Namespaces
Variants

Conditional inclusion

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous
Preprocessor
#if #ifdef #ifndef #else #elif #elifdef #elifndef #endif
(C++23) (C++23)
(C++26)

预处理器支持对源文件部分内容进行条件编译。该行为由 #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 表达式,用于检测头文件或源文件是否存在。
(since C++20)
  • __has_embed 表达式,用于检测资源是否可供嵌入。
(since C++26)

在上述语境中,提及的标识符会被视为已定义宏的名称。

(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

#elifdef identifier 本质上等价于 #elif defined identifier

#elifndef identifier 本质上等价于 #elif !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 文档 关于 条件包含