Spec-Zone.ru › C++

Условное включение

Препроцессор поддерживает условную компиляцию частей исходного файла. Это поведение контролируется директивами #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 и __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 не должно быть хорошо сформированным, в то время как #elif's cond2 должно быть допустимым выражением. Начиная с CWG 1955, #elif ведущее пропущенный блок кода также пропускается.

Объединённые директивы

Проверяет, был ли идентификатор определён как имя макроса.

#ifdef идентификатор по существу эквивалентно #if defined идентификатор.

#ifndef идентификатор по существу эквивалентно #if !defined идентификатор.

#elifdef идентификатор по существу эквивалентно #elif defined идентификатор.

#elifndef идентификатор по существу эквивалентно #elif !defined идентификатор.

(с 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 пропускается

См. также

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

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/preprocessor/conditional

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API