Conditional inclusion - cppreference.com
The preprocessor supports conditional inclusion of parts of source file. This behavior is controlled by the following directives.
Syntax
#if constant-expression
|
(1) | ||||||||
#ifdef identifier
|
(2) | ||||||||
#ifndef identifier
|
(3) | ||||||||
#elif constant-expression
|
(4) | ||||||||
#elifdef identifier
|
(5) | (since C++23) | |||||||
#elifndef identifier
|
(6) | (since C++23) | |||||||
#else
|
(7) | ||||||||
#endif
|
(8) | ||||||||
1) Includes the code block it controls if constant-expression evaluates to a nonzero value.
2) Equivalent to #if defined identifier .
3) Equivalent to #if !defined identifier .
4) Includes the code block it controls if the previous #if block and all intervening #elif blocks are skipped, and constant-expression evaluates to a nonzero value.
5) Equivalent to #elif defined identifier .
6) Equivalent to #elif !defined identifier .
7) Includes the code block it controls if the previous #if block and all intervening #elif blocks are skipped.
8) Terminates the current #if, #elif or #else block.
Explanation
The conditional preprocessing block starts with an #if, #ifdef or #ifndef directive, then optionally includes any number of #elif, #elifdef, or #elifndef(since C++23) directives, then optionally includes at most one #else directive and is terminated with #endif directive. Any nested conditional preprocessing blocks are processed separately.
Except for #endif, each of these directives control the code block until the first #elif, #elifdef, #elifndef(since C++23), #else or #endif directive not belonging to any nested conditional preprocessing blocks.
#if, #ifdef and #ifndef directives test the specified condition (see below):
- If it evaluates to true (nonzero value), the controlled code block is included. In this case, all subsequent
#else,#elifdef,#elifndef,(since C++23) and#elifblocks till the associated#endifdirective are skipped. - Otherwise, the controlled code block is skipped. In this case:
- If the subsequent non-nested directive is
#endif, no code block is included. - If the subsequent non-nested directive is
#else, the code block it controls is unconditionally included. - Otherwise, the subsequent non-nested (
#elif,#elifdef, or#elifndef(since C++23)) directive acts as if it was an#ifdirective: checks for condition, includes or skips the controlled code block based on the result, and in the latter case processes its subsequent directive.
- If the subsequent non-nested directive is
#define MY_MACRO 2 #if MY_MACRO > 0 // included #elif MY_MACRO > 1 // the condition of the #if-block is passed, no more tests // not included #endif
Testing conditions
Preprocessing-exclusive expressions
constant-expression may contain the following expressions:
definedexpressions (see below), which detects whether an identifier is defined as a macro name.
|
(since C++17) |
|
(since C++20) |
|
(since C++26) |
defined expressions
defined identifier
|
(1) | ||||||||
defined( identifier )
|
(2) | ||||||||
The defined expression evaluates to 1 if identifier is currently defined as macro name, or 0 if it is not.
An identifier is defined as macro name in the context of condition inclusion directive if any of the following conditions is satisfied:
- It has one or more active macro definitions.
- It is predefined.
|
(since C++17) |
|
(since C++20) |
|
(since C++26) |
Macro expansion
Except for the identifier s of defined expressions, macros in constant-expression s will be expanded prior to condition evaluation.
If the preprocessing token defined is generated as a result of this replacement process, or use of the defined unary operator does not match one of the two specified forms prior to macro replacement, the behavior is undefined(until C++26)the program is ill-formed, no diagnostic required(since C++26).
#define MACRO defined // UB until C++26, IFNDR since C++26 #if MACRO MACRO // “defined” is generated by macro expansion #if defined (A + B) // “A + B” is not an identifier #if defined 1 // 1 is a literal, not an identifier #if defined // no argument provided
|
If an identifier #define limit 0 #define HAS_EMBED_LIMIT_10(file) __has_embed(file limit(10)) // ill-formed #if __has_embed("e.dat" limit(10)) // “limit” is defined as a macro #if HAS_EMBED("e.dat") // macro expansion encounters the macro “limit” |
(since C++26) |
Condition evaluation
After all macro expansion and evaluations of the preprocessing-exclusive expressions described above, each remaining identifier other than true and false is replaced with the preprocessing number 0 (this includes identifiers that are lexically keywords, but not alternative tokens like and).
After that, each preprocessing token is converted into a token. The resulting tokens comprise the condition controlloing the associated code block, which is evaluated as an constant expression, with the following changes:
- During token conversion and condition evaluation (including determining the value of character literals):
|
(until C++11) |
|
(since C++11) |
- Each subexpression with type
boolis subjected to integral promotion before processing continues.
If the condition evaluates to nonzero value, the controlled code block is included and skipped otherwise.
Notes
While #elifdef and #elifndef directives target C++23, implementations are encouraged to backport them to the older language modes as conforming extensions.
Example
#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 }
Possible output:
1: yes 2: yes 3: yes 4: no! 4: yes
Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
| DR | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| CWG 1955 | C++98 | #elif must have a valid expression even ifit followed a #if/#elif block whose condition was true
|
the expression of #elif is skipped in this case
|