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