if
statement
Условно выполняет другой оператор.
Используется там, где код должен быть выполнен на основе условия , или когда if оператор вычисляется в явно константно-вычисляемом контексте (since C++23) .
Содержание |
Синтаксис
attr
(необязательно)
if
constexpr
(необязательно)
(
init-statement
(необязательно)
condition
)
statement-true
|
(1) | ||||||||
attr
(необязательно)
if
constexpr
(необязательно)
(
init-statement
(необязательно)
condition
)
statement-true
else
statement-false
|
(2) | ||||||||
attr
(необязательно)
if
!
(необязательно)
consteval
compound-statement
|
(3) | (начиная с C++23) | |||||||
attr
(необязательно)
if
!
(необязательно)
consteval
compound-statement
else
statement
|
(4) | (начиная с C++23) | |||||||
| attr | - | (since C++11) любое количество атрибутов | ||
constexpr
|
- | (since C++17) если присутствует, оператор становится constexpr if оператором | ||
| init-statement | - |
(since C++17)
либо
Обратите внимание, что любой init-statement должен заканчиваться точкой с запятой. Поэтому его часто неформально описывают как выражение или объявление, за которым следует точка с запятой. |
||
| condition | - | условие | ||
| statement-true | - | оператор , который выполняется, если condition возвращает true | ||
| statement-false | - | оператор, который выполняется, если condition возвращает false | ||
| compound-statement | - |
составной оператор
, который выполняется, если оператор
if
вычисляется в
контексте явного постоянного вычисления
(или не вычисляется в таком контексте, если перед
consteval
стоит
!
)
|
||
| statement | - |
оператор (должен быть составным оператором, см.
ниже
), который выполняется, если оператор
if
не вычисляется в контексте явного постоянного вычисления (или вычисляется в таком контексте, если перед
consteval
стоит
!
)
|
Условие
Условием может быть либо condition , либо expression , либо simple declaration .
|
(since C++26) |
- Если это может быть синтаксически разрешено как выражение, оно трактуется как выражение. В противном случае оно трактуется как объявление которое не является объявлением структурированной привязки (начиная с C++26) .
Когда управление достигает условия, условие даёт значение, которое используется для определения того, в какую ветвь перейдёт управление.
Выражение
Если condition является выражением, возвращаемое им значение — это значение выражения, контекстно преобразованное в bool . Если это преобразование некорректно, программа является некорректной.
Объявление
Если condition является простым объявлением, возвращаемое значение - это значение переменной решения (см. ниже), контекстно преобразованное в bool . Если это преобразование некорректно, программа является некорректной.
Объявление без использования структурированных привязок
Объявление имеет следующие ограничения:
- Синтаксически соответствует следующей форме:
|
(до C++11) |
|
(начиная с C++11) |
- Декларатор не может определять функцию или массив .
- последовательность спецификаторов типа (до C++11) последовательность спецификаторов объявления может содержать только спецификаторы типа и constexpr , и она (начиная с C++11) не может определять класс или перечисление .
Переменная решения объявления - это объявленная переменная.
Декларация структурированной привязкиДекларация имеет следующие ограничения:
Переменной решения декларации является введённая переменная e введённая декларацией . |
(начиная с C++26) |
Выбор ветки
Если условие возвращает true , выполняется statement-true .
Если else часть оператора if присутствует и условие возвращает false , statement-false выполняется.
Если присутствует else часть оператора if и statement-true также является оператором if , то этот внутренний оператор if также должен содержать часть else (другими словами, во вложенных операторах if , else связывается с ближайшим оператором if , который еще не имеет связанной части else ).
#include <iostream> int main() { // простой оператор if с веткой else int i = 2; if (i > 2) std::cout << i << " is greater than 2\n"; else std::cout << i << " is not greater than 2\n"; // вложенный оператор if int j = 1; if (i > 1) if (j > 2) std::cout << i << " > 1 and " << j << " > 2\n"; else // этот else относится к if (j > 2), а не к if (i > 1) std::cout << i << " > 1 and " << j << " <= 2\n"; // объявления могут использоваться как условия с dynamic_cast struct Base { virtual ~Base() {} }; struct Derived : Base { void df() { std::cout << "df()\n"; } }; Base* bp1 = new Base; Base* bp2 = new Derived; if (Derived* p = dynamic_cast<Derived*>(bp1)) // приведение не удается, возвращает nullptr p->df(); // не выполняется if (auto p = dynamic_cast<Derived*>(bp2)) // приведение успешно p->df(); // выполняется }
Вывод:
2 is not greater than 2 2 > 1 and 1 <= 2 df()
if инструкции с инициализаторомЕсли используется init-statement , инструкция if эквивалентна
или
За исключением того, что имена, объявленные в init-statement (если init-statement является объявлением) и имена, объявленные в condition (если condition является объявлением), находятся в одной области видимости, которая также является областью видимости обоих statement ов. std::map<int, std::string> m; std::mutex mx; extern bool shared_flag; // guarded by mx int demo() { if (auto it = m.find(10); it != m.end()) return it->second.size(); if (char buf[10]; std::fgets(buf, 10, stdin)) m[0] += buf; if (std::lock_guard lock(mx); shared_flag) { unsafe_ping(); shared_flag = false; } if (int s; int count = ReadBytesWithSignal(&s)) { publish(count); raise(s); } if (const auto keywords = {"if", "for", "while"}; std::ranges::any_of(keywords, [&tok](const char* kw) { return tok == kw; })) { std::cerr << "Token must not be a keyword\n"; } } |
(начиная с C++17) | ||||||||||||||||||||||||||||||||||||||||||||||
Constexpr ifУтверждение, начинающееся с if constexpr , известно как constexpr if statement . Все подутверждения constexpr if statement являются control-flow-limited statements . В операторе constexpr if условие должно быть контекстно преобразуемым константным выражением типа bool (до C++23) выражением, контекстно преобразуемым в bool , где преобразование является константным выражением (начиная с C++23) . Если condition возвращает true , тогда statement-false отбрасывается (если присутствует), в противном случае statement-true отбрасывается. Операторы return в отброшенном операторе не участвуют в определении типа возвращаемого значения функции: template<typename T> auto get_value(T t) { if constexpr (std::is_pointer_v<T>) return *t; // выводит тип возвращаемого значения как int для T = int* else return t; // выводит тип возвращаемого значения как int для T = int } Отброшенное выражение может ODR-использовать переменную, которая не определена: extern int x; // определение x не требуется int f() { if constexpr (true) return 0; else if (x) return x; else return -x; } Вне шаблона, отброшенное выражение полностью проверяется. if constexpr не является заменой для #if директивы препроцессора: void f() { if constexpr(false) { int i = 0; int *p = i; // Ошибка, даже в отброшенном операторе } } Если constexpr if оператор появляется внутри шаблонной сущности , и если условие не является value-dependent после инстанцирования, отброшенный оператор не инстанцируется при инстанцировании охватывающего шаблона. template<typename T, typename ... Rest> void g(T&& p, Rest&& ...rs) { // ... обработка p if constexpr (sizeof...(rs) > 0) g(rs...); // никогда не инстанцируется с пустым списком аргументов } Условие остается зависимым от значения после инстанцирования, если это вложенный шаблон: template<class T> void g() { auto lm = [=](auto p) { if constexpr (sizeof(T) == 1 && sizeof p == 1) { // это условие остается зависимым от значений после инстанцирования g<T>, // что влияет на неявные захваты лямбды // этот составной оператор может быть отброшен только после // инстанцирования тела лямбды } }; } Отброшенное утверждение не может быть некорректным для каждой возможной специализации: template<typename T> void f() { if constexpr (std::is_arithmetic_v<T>) // ... else { using invalid_array = int[-1]; // некорректно: недопустимо для любого T static_assert(false, "Must be arithmetic"); // некорректно до CWG2518 } } Распространенным обходным решением до реализации CWG issue 2518 для таких перехватывающих все случаев операторов было использование зависящего от типа выражения, которое всегда false : template<typename> constexpr bool dependent_false_v = false; template<typename T> void f() { if constexpr (std::is_arithmetic_v<T>) // ... else { // обходное решение до CWG2518 static_assert(dependent_false_v<T>, "Must be arithmetic"); } } Объявление typedef или объявление псевдонима (начиная с C++23) может использоваться в качестве init-statement constexpr if для ограничения области видимости псевдонима типа.
|
(начиная с C++17) |
Consteval ifУтверждение, начинающееся с if consteval , известно как consteval if утверждение . Все подутверждения consteval if утверждения являются ограниченными по потоку управления утверждениями . statement должен быть составным оператором, и он все равно будет рассматриваться как часть константного оператора if, даже если он не является составным оператором (и, следовательно, приводит к ошибке компиляции):
Запустить этот код
constexpr void f(bool b) { if (true) if consteval {} else ; // ошибка: не составной оператор // else не связан с внешним if } Если выражение consteval if вычисляется в контексте явного постоянного вычисления , compound-statement выполняется. В противном случае, statement выполняется, если он присутствует. Если оператор начинается с if ! consteval , то составной-оператор и оператор (если присутствует) должны быть составными операторами. Такие операторы не считаются consteval if операторами, но эквивалентны им:
составной-оператор в consteval if операторе (или оператор в отрицательной форме) находится в контексте непосредственной функции , где вызов непосредственной функции не обязан быть константным выражением.
Запустить этот код
#include <cmath> #include <cstdint> #include <cstring> #include <iostream> constexpr bool is_constant_evaluated() noexcept { if consteval { return true; } else { return false; } } constexpr bool is_runtime_evaluated() noexcept { if not consteval { return true; } else { return false; } } consteval std::uint64_t ipow_ct(std::uint64_t base, std::uint8_t exp) { if (!base) return base; std::uint64_t res{1}; while (exp) { if (exp & 1) res *= base; exp /= 2; base *= base; } return res; } constexpr std::uint64_t ipow(std::uint64_t base, std::uint8_t exp) { if consteval // использовать алгоритм, дружественный к вычислению на этапе компиляции { return ipow_ct(base, exp); } else // использовать вычисление во время выполнения { return std::pow(base, exp); } } int main(int, const char* argv[]) { static_assert(ipow(0, 10) == 0 && ipow(2, 10) == 1024); std::cout << ipow(std::strlen(argv[0]), 3) << '\n'; } |
(начиная с C++23) |
Примечания
Если statement-true или statement-false не является составным оператором, он обрабатывается как если бы был:
if (x) int i; // переменная i больше не в области видимости
то же самое, что
if (x) { int i; } // переменная i больше не в области видимости
Область видимости имени, введенного condition , если это объявление, является объединенной областью видимости тел обоих операторов:
if (int x = f()) { int x; // ошибка: переопределение x } else { int x; // ошибка: переопределение x }
Если
statement-true
выполняется через
goto
или
longjmp
,
condition
не вычисляется и
statement-false
не выполняется.
|
Встроенные преобразования не разрешены в условии constexpr if выражения, за исключением не- сужающих целочисленных преобразований к bool . |
(начиная с C++17)
(до C++23) |
| Макрос тестирования возможностей | Значение | Стандарт | Возможность |
|---|---|---|---|
__cpp_if_constexpr
|
201606L
|
(C++17) |
constexpr
if
|
__cpp_if_consteval
|
202106L
|
(C++23) |
consteval
if
|
Ключевые слова
if , else , constexpr , consteval
Отчеты о дефектах
Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.
| DR | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| CWG 631 | C++98 |
управление потоком выполнения было неопределенным, если
первое подвыражение достигается через метку |
условие не вычисляется и второе
подвыражение не выполняется (так же как в C) |
Смотрите также
|
(C++20)
|
определяет, происходит ли вызов в контексте константного вычисления
(функция) |
|
Документация C
для
if
statement
|
|