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 .

expression также может содержать следующие выражения:

  • __has_include выражения, которые определяют существование заголовочного или исходного файла.
  • __has_cpp_attribute выражения, которые определяют поддержку заданного токена атрибута и его поддерживаемую версию.
(since C++20)
  • __has_embed выражения, которые определяют доступность ресурса для встраивания.
(since C++26)

Упомянутые выше идентификаторы обрабатываются так, как если бы они были именами определенных макросов в данном контексте.

(since C++17)

После всех макроподстановок и вычислений defined и выражений, описанных выше, любой идентификатор, не являющийся boolean literal , заменяется числом 0 (это включает идентификаторы, которые являются лексическими ключевыми словами, но не альтернативными токенами, такими как and ).

Тогда выражение вычисляется как integral constant expression .

Если выражение вычисляется в ненулевое значение, управляемый блок кода включается, в противном случае пропускается.

Примечание: До разрешения 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
// Обратите внимание, что если компилятор не поддерживает директивы #elifdef/#elifndef из C++23
// то будет выбран "неожиданный" блок (см. ниже).
#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 только если поддерживаются директивы
// #elifdef/#elifndef из C++23.
#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++.

DR Применяется к Поведение в опубликованной версии Корректное поведение
CWG 1955 C++98 неудачный #elif требовал корректного выражения неудачный #elif пропускается

Смотрите также

Документация по C для Условное включение