Условное включение
Препроцессор поддерживает условную компиляцию частей исходного файла. Такое поведение контролируется директивами #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).
Если выражение даёт ненулевое значение, контролируемый блок кода включается и, в противном случае, пропускается.
|
В контексте директивы препроцессора выражение |
(начиная с 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 идентификатор.
|
|
(начиная с 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 по Условное включение
|