命名空间
变体
操作

条件包含

来自 cppreference.com
 
 
C++ 语言
一般主题
流程控制
条件执行语句
if
迭代语句(循环)
for
范围-for (C++11)
跳转语句
函数
函数声明
lambda 函数表达式
inline 说明符
动态异常规范 (直到 C++17*)
noexcept 说明符 (C++11)
异常
命名空间
类型
说明符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
存储持续时间说明符
初始化
 
预处理器
#if#ifdef#ifndef#else#elif#elifdef#elifndef#endif
(C++23)(C++23)
 

预处理器支持对源文件部分进行条件编译。 此行为由 #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

The expression may contain

  • 一元运算符,形式为 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 不需要是格式良好的,而 #elifcond2 必须是有效的表达式。根据 CWG 1955,导致跳过代码块的 #elif 也将被跳过。

[edit] 组合指令

检查标识符是否 被定义为宏名称

#ifdef 标识符 本质上等效于 #if defined 标识符

#ifndef 标识符 本质上等效于 #if !defined 标识符

#elifdef 标识符 本质上等效于 #elif defined 标识符

#elifndef 标识符 本质上等效于 #elif !defined 标识符

(自 C++23 起)

[edit] 注意

虽然 #elifdef#elifndef 指令针对 C++23,但鼓励实现将它们作为符合扩展移植到旧的语言模式。

[edit] 示例

#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

[edit] 缺陷报告

以下行为更改缺陷报告被追溯应用于先前发布的 C++ 标准。

DR 应用于 发布时的行为 正确行为
CWG 1955 C++98 失败的 #elif 表达式要求有效 失败的 #elif 被跳过

[edit] 另请参阅

C 文档 针对 条件包含