Оператор 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 значением условия должно быть контекстно преобразованное константное выражение типа Операторы 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;
}
Вне шаблона отброшенный оператор полностью проверяется. 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-для-всех это выражение, зависящее от типа, которое всегда 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, за исключением не-сужающих целочисленных преобразований в |
(начиная с 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 | |