Пространства имён
Варианты
Действия

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

Материал из cppreference.com
 
 
Язык С++
Общие темы
Управление потоком
Операторы условного выполнения
if
Операторы итерации (циклы)
Операторы перехода
Функции
Объявление функции
Выражение лямбда-функции
Спецификатор inline
Спецификации динамических исключений (до C++17*)
Спецификатор noexcept (C++11)
Исключения
Пространства имён
Типы
Спецификаторы
decltype (C++11)
auto (C++11)
alignas (C++11)
Спецификаторы длительности хранения
Инициализация
Выражения
Альтернативные представления
Литералы
Логические - Целочисленные - С плавающей запятой
Символьные - Строковые - nullptr (C++11)
Определяемые пользователем (C++11)
Утилиты
Атрибуты (C++11)
Types
Объявление typedef
Объявление псевдонима типа (C++11)
Casts
Неявные преобразования - Явные преобразования
static_cast - dynamic_cast
const_cast - reinterpret_cast
Выделение памяти
Классы
Свойства функции класса
explicit (C++11)
static
Специальные функции-элементы
Шаблоны
Разное
 
Препроцессор
#if#ifdef#ifndef#else#elif#elifdef#elifndef#endif
(C++23)(C++23)
 

Препроцессор поддерживает условную компиляцию частей исходного файла. Такое поведение контролируется директивами #if, #else, #elif, #ifdef, #ifndef и #endif.

Содержание

[править] Синтаксис

#if выражение
#ifdef идентификатор
#ifndef идентификатор
#elif выражение
#elifdef идентификатор (начиная с C++23)
#elifndef идентификатор (начиная с C++23)
#else
#endif

[править] Объяснение

Блок условной предварительной обработки начинается с директивы #if, #ifdef или #ifndef, затем необязательно включает любое количество директив #elif, затем необязательно включает не более одной директивы #else и завершается директивой #endif. Любые внутренние блоки условной предварительной обработки обрабатываются отдельно.

Каждая директива #if, #elif, #else, #ifdef и #ifndef управляет блоком кода до тех пор, пока первая директива #elif, #else, #endif не будет принадлежать никаким внутренним блокам условной предварительной обработки.

Директивы #if, #ifdef и #ifndef проверяют указанное условие (смотрите ниже) и, если оно истинно, компилируется контролируемый блок кода. В этом случае последующие директивы #else и #elif игнорируются. В противном случае, если указанное условие оценивается как ложное, управляемый блок кода пропускается, и обрабатывается последующая директива #else или #elif (если таковая имеется). В первом случае блок кода, управляемый директивой #else, компилируется безусловно. В последующем случае директива #elif действует так же, как если бы она была директивой #if: проверяет условие, компилирует или пропускает контролируемый блок кода в зависимости от результата, и в дальнейшем обрабатывает последующие директивы #elif и #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), любой идентификатор, не являющийся логическим литералом, заменяется числом 0 (сюда входят идентификаторы, которые лексически являются ключевыми словами, но не альтернативные маркеры, такие как and).

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

В контексте директивы препроцессора выражение __has_cpp_attribute определяет, поддерживается ли данный маркер атрибута, и его поддерживаемую версию. Смотрите Проверка атрибутов.

(начиная с C++20)

Примечание: До решения CWG проблема 1955, #if cond1 ... #elif cond2 отличается от #if cond1 ... #else, за которым следует #if cond2, потому что, если cond1 истинно, второй #if пропускается и cond2 не обязательно должно иметь правильный формат, тогда как выражение cond2 для всех #elif должно быть допустимым выражением. Начиная с 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: да\n";
#else
    std::cout << "1: нет\n";
#endif
 
#ifndef ABCD
    std::cout << "2: нет1\n";
#elif ABCD == 2
    std::cout << "2: yes\n";
#else
    std::cout << "2: нет2\n";
#endif
 
#if !defined(DCBA) && (ABCD < 2*4-3)
    std::cout << "3: да\n";
#endif
 
 
// Обратите внимание, что если компилятор не поддерживает директивы C++23
// #elifdef/#elifndef, тогда будет выбран "неожидаемый" блок (смотрите ниже).
#ifdef CPU
    std::cout << "4: нет1\n";
#elifdef GPU
    std::cout << "4: нет2\n";
#elifndef RAM
    std::cout << "4: да\n"; // ожидаемый блок
#else
    std::cout << "4: нет!\n"; // неожиданно выбирает этот блок, пропуская 
                             // неизвестные директивы и "перескакивая"
                             // прямо с "#ifdef CPU" на этот блок "#else"
#endif
 
// Чтобы решить указанную выше проблему, мы можем условно определить
// макрос ELIFDEF_SUPPORTED, только если поддерживаются директивы C++23
// #elifdef/#elifndef.
#if 0
#elifndef UNDEFINED_MACRO
#define ELIFDEF_SUPPORTED
#else
#endif
 
#ifdef ELIFDEF_SUPPORTED
    #ifdef CPU
        std::cout << "4: нет1\n";
    #elifdef GPU
        std::cout << "4: нет2\n";
    #elifndef RAM
        std::cout << "4: да\n"; // ожидаемый блок
    #else
        std::cout << "4: нет3\n";
    #endif
#else // когда #elifdef не поддерживается, используйте старый подробный `#elif defined`
    #ifdef CPU
        std::cout << "4: нет1\n";
    #elif defined GPU
        std::cout << "4: нет2\n";
    #elif !defined RAM
        std::cout << "4: да\n"; // ожидаемый блок
    #else
        std::cout << "4: нет3\n";
    #endif
#endif
}

Возможный вывод:

1: да
2: да
3: да
4: нет!
4: да

[править] Отчёты о дефектах

Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:

Номер Применён Поведение в стандарте Корректное поведение
CWG 1955 C++98 некорректны выражения #elif, которые должны были быть действительными некорректные #elif пропускаются

[править] Смотрите также

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