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