跳到主要内容

条件包含

预处理器支持源文件部分的条件编译。此行为由以下指令控制:#if, #ifdef, #ifndef, #else , #elif#elifdef, #elifndef (自 C++23 起),和#endif指令。

语法

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 identifierdefined (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 不需要是合法的,而 #elifcond2 必须是一个有效的表达式。根据 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 起)

备注

虽然#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 被跳过

条件包含

预处理器支持源文件部分的条件编译。此行为由以下指令控制:#if, #ifdef, #ifndef, #else , #elif#elifdef, #elifndef (自 C++23 起),和#endif指令。

语法

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 identifierdefined (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 不需要是合法的,而 #elifcond2 必须是一个有效的表达式。根据 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 起)

备注

虽然#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 被跳过