Оператор if
Условно выполняет другой оператор.
Используется там, где код должен быть выполнен на основе условия времени выполнения или времени компиляции (начиная с C++17), или независимо от того, вычисляется ли оператор if в контексте с явно вычисляемой константой. (начиная с C++23).
Содержание |
[править] Синтаксис
атрибуты (необязательно) if constexpr (необязательно) ( оператор-инициализации (необязательно) условие ) оператор-true
|
(1) | ||||||||
атрибуты (необязательно) if constexpr (необязательно) ( оператор-инициализации (необязательно) условие ) оператор-true else оператор-false
|
(2) | ||||||||
атрибуты (необязательно) if ! (необязательно) consteval составной-оператор
|
(3) | (начиная с C++23) | |||||||
атрибуты (необязательно) if ! (необязательно) consteval составной-оператор else оператор
|
(4) | (начиная с C++23) | |||||||
атрибуты | — | (начиная с C++11) любое количество атрибутов | ||
constexpr
|
— | (начиная с C++17) если присутствует, оператор становится constexpr if оператором | ||
оператор-инициализации | — | (начиная с C++17) одно из следующего
| ||
условие | — | одно из
| ||
утверждение-истинно | — | любой оператор (часто составной оператор), который выполняется, если условие оценивается как true | ||
утверждение-ложно | — | любой оператор (часто составной оператор), который выполняется, если условие оценивается как false | ||
составное выражение | — | любой составной оператор, который выполняется, если оператор if
| ||
оператор | — | любой оператор (должен быть составным оператором, смотрите ниже), который выполняется, если оператор if
|
[править] Объяснение
Если условие возвращает true после преобразования в bool, выполняется утверждение-истинно.
Если часть else оператора if присутствует и условие возвращает false после преобразования в bool, выполняется утверждение-ложно.
Во второй форме оператора if (та, которая включает else), если утверждение-истинно также является оператором if, то этот внутренний оператор if также должен содержать часть else (другими словами, во вложенных операторах if, else связан с ближайшим if, у которого нет else).
#include <iostream> int main() { // простой оператор if с оператором else int i = 2; if (i > 2) std::cout << i << " больше чем 2\n"; else std::cout << i << " не больше чем 2\n"; // вложенный оператор if int j = 1; if (i > 1) if (j > 2) std::cout << i << " > 1 и " << j << " > 2\n"; else // это else является частью if (j > 2), а не if (i > 1) std::cout << i << " > 1 и " << 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 не больше чем 2 2 > 1 и 1 <= 2 df()
Операторы if с инициализаторомЕсли используется оператор-инициализации, оператор if эквивалентен
или
За исключением того, что имена, объявленные оператором инициализации (если оператор инициализации является объявлением), и имена, объявленные условием (если условие является объявлением), находятся в одной и той же области видимости, которая также является областью видимости обоих операторов. std::map<int, std::string> m; std::mutex mx; extern bool shared_flag; // под охраной 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 << "Токен не должен быть ключевым словом\n"; } } |
(начиная с C++17) |
Constexpr ifОператор, начинающийся с В операторе constexpr if значением условия должно быть контекстно преобразованное константное выражение типа bool (до C++23)выражение, контекстуально преобразованное в bool, где преобразование представляет собой константное выражение (начиная с C++23). Если значение равно 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 появляется внутри шаблонной сущности, и если условие не зависит от значения после создания экземпляра, отброшенный оператор не создаётся при создании экземпляра включающего шаблона. 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 { // некорректо: недействителен для каждого T static_assert(false, "Должен быть арифметическим"); using invalid_array = int[-1]; // некорректо: недействителен для каждого T static_assert(false, "Должен быть арифметическим"); // некорректо до CWG2518 } } Обычный обходной путь перед реализацией CWG2518 для такого оператора catch-для-всех это выражение, зависящее от типа, которое всегда false: template<typename> inline 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>, "Должен быть арифметическим"); // ok } } Метки (goto цели, метки Примечание: объявление typedef или объявление псевдонима (начиная с C++23) можно использовать в качестве оператора инициализации оператора constexpr if, чтобы уменьшить область видимости псевдонима типа.
|
(начиная с C++17) |
Consteval ifОператор, начинающийся с
и составной-оператор и оператор (если есть) должны быть составными операторами. Если оператор не является составным оператором, он всё равно будет рассматриваться как часть оператора consteval if (и, таким образом, приведёт к ошибке компиляции): Запустить этот код constexpr void f(bool b) { if (true) if consteval {} else ; // ошибка: не составной оператор // else не связан с внешним if } Если оператор 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) |
[править] Примечание
Если утверждение-истина или утверждение-ложь не является составным утверждением, оно обрабатывается так, как если бы оно было:
if (x) int i; // i больше не в области видимости
так же как
if (x) { int i; } // i больше не в области видимости
Область видимости имени, представленного условием, если это объявление, является объединенной областью видимости тел обоих операторов:
if (int x = f()) { int x; // ошибка: повторное объявление x } else { int x; // ошибка: повторное объявление x }
Если утверждение-истина вводится goto
или longjmp, условие не оценивается, а утверждение-ложь не выполняется.
Встроенные преобразования не допускаются в условии оператора constexpr if, за исключением не-сужающих целочисленных преобразований в bool. |
(начиная с C++17) (до C++23) |
|
(начиная с C++17) |
Макрос тест функциональности | Значение | Стандарт | Комментарий |
---|---|---|---|
__cpp_if_constexpr |
201606L | (C++17) | constexpr if
|
__cpp_if_consteval |
202106L | (C++23) | consteval if
|
[править] Ключевые слова
if, else, constexpr, consteval
[править] Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
Номер | Применён | Поведение в стандарте | Корректное поведение |
---|---|---|---|
CWG 631 | C++98 | поток управления не был указан, если первый подоператор достигается через метку |
условие не оценивается и второй подоператор не выполняется (так же, как в C) |
[править] Смотрите также
(C++20) |
определяет, происходит ли вызов в контексте вычисления константы (функция) |
Документация C по Оператор
if |