Conditional inclusion
Препроцессор поддерживает условную компиляцию частей исходного файла. Это поведение контролируется директивами
#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 также может содержать следующие выражения:
Упомянутые выше идентификаторы обрабатываются так, как если бы они были именами определенных макросов в данном контексте. |
(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
.
|
|
(начиная с 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
для
Условное включение
|