if инструкция
Условно выполняет другую инструкцию.
Используется, когда код должен выполняться в зависимости от условия или от того, вычисляется ли инструкция if в контексте, явно вычисляемом во время компиляции(начиная с 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 | - | (начиная с C++11) любое количество атрибутов | ||
constexpr | - | (начиная с C++17) если присутствует, инструкция становится инструкцией constexpr if | ||
| init-statement | - |
(начиная с C++17) одно из следующего:
Обратите внимание, что любой init-statement должен заканчиваться точкой с запятой. Поэтому его часто неформально описывают как выражение или объявление, за которым следует точка с запятой. |
||
| condition | - | условие | ||
| statement-true | - | инструкция, выполняемая, если condition дает true |
||
| statement-false | - | инструкция, выполняемая, если condition дает false |
||
| compound-statement | - | составная инструкция, выполняемая, если инструкция if вычисляется в контексте, явно вычисляемом во время компиляции (или не вычисляется в таком контексте, если перед consteval стоит !) |
||
| statement | - | инструкция (должна быть составной инструкцией, см. ниже), выполняемая, если инструкция if не вычисляется в контексте, явно вычисляемом во время компиляции (или вычисляется в таком контексте, если перед consteval стоит !) |
Условие
condition может быть выражением (expression) или простым объявлением.
| (начиная с C++26) |
- Если его можно синтаксически разобрать как выражение, оно рассматривается как выражение. В противном случае оно рассматривается как объявление, не являющееся объявлением структурированной привязки(начиная с C++26).
При достижении условия оно дает значение, которое используется для определения того, в какую ветвь перейдет управление.
Выражение
Если condition является выражением, полученное им значение — это значение выражения, контекстно преобразованное в bool. Если такое преобразование некорректно, программа некорректна.
Объявление
Если condition является простым объявлением, полученное им значение — это значение переменной выбора (см. ниже), контекстно преобразованное в bool. Если такое преобразование некорректно, программа некорректна.
Объявление, не являющееся объявлением структурированной привязки
Объявление должно удовлетворять следующим ограничениям:
- Синтаксически соответствует следующей форме:
| (до C++11) |
| (начиная с C++11) |
- Декларатор не может указывать на функцию или массив.
- последовательность спецификаторов типа(до C++11)последовательность спецификаторов объявления может содержать только спецификаторы типа и constexpr, а также(начиная с C++11) не может определять класс или перечисление.
Переменная выбора этого объявления — объявленная переменная.
Объявление структурированной привязкиОбъявление должно удовлетворять следующим ограничениям:
Переменная выбора этого объявления — вымышленная переменная | (начиная с C++26) |
Выбор ветви
Если condition дает true, выполняется statement-true.
Если часть else инструкции if присутствует и condition дает false, выполняется statement-false.
Если часть else инструкции if присутствует, а statement-true также является инструкцией if, эта вложенная инструкция if тоже должна содержать часть else (иначе говоря, во вложенных инструкциях if часть else относится к ближайшей инструкции if, с которой еще не связана часть else).
#include <iostream>
int main()
{
// simple if-statement with an else clause
int i = 2;
if (i > 2)
std::cout << i << " is greater than 2\n";
else
std::cout << i << " is not greater than 2\n";
// nested if-statement
int j = 1;
if (i > 1)
if (j > 2)
std::cout << i << " > 1 and " << j << " > 2\n";
else // this else is part of if (j > 2), not of if (i > 1)
std::cout << i << " > 1 and " << j << " <= 2\n";
// declarations can be used as conditions with 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)) // cast fails, returns nullptr
p->df(); // not executed
if (auto p = dynamic_cast<Derived*>(bp2)) // cast succeeds
p->df(); // executed
}
Вывод:
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Инструкция, начинающаяся с ifconstexpr, называется инструкцией constexpr if. Все подчиненные инструкции constexpr if являются инструкциями с ограниченным потоком управления. В инструкции constexpr if condition должно быть контекстно преобразуемым константным выражением типа bool(до C++23)выражением, контекстно преобразуемым в bool, причем преобразование должно быть константным выражением(начиная с C++23). Если condition дает Инструкции return в отброшенной инструкции не участвуют в выведении возвращаемого типа функции: template<typename T>
auto get_value(T t)
{
if constexpr (std::is_pointer_v<T>)
return *t; // deduces return type to int for T = int*
else
return t; // deduces return type to int for T = int
}
Отброшенная инструкция может использовать по ODR переменную, которая не определена: extern int x; // no definition of x required
int f()
{
if constexpr (true)
return 0;
else if (x)
return x;
else
return -x;
}
Вне шаблона отброшенная инструкция полностью проверяется. ifconstexpr не заменяет директиву препроцессора void f()
{
if constexpr(false)
{
int i = 0;
int *p = i; // Error even though in discarded statement
}
}
Если инструкция constexpr if находится внутри шаблонной сущности и после инстанцирования condition не является зависимым от значения, отброшенная инструкция не инстанцируется при инстанцировании охватывающего шаблона. template<typename T, typename ... Rest>
void g(T&& p, Rest&& ...rs)
{
// ... handle p
if constexpr (sizeof...(rs) > 0)
g(rs...); // never instantiated with an empty argument list
}
Условие остается зависимым от значения после инстанцирования во вложенном шаблоне: template<class T>
void g()
{
auto lm = [=](auto p)
{
if constexpr (sizeof(T) == 1 && sizeof p == 1)
{
// this condition remains value-dependent after instantiation of g<T>,
// which affects implicit lambda captures
// this compound statement may be discarded only after
// instantiation of the lambda body
}
};
}
Отброшенная инструкция не может быть некорректной для всех возможных специализаций: template<typename T>
void f()
{
if constexpr (std::is_arithmetic_v<T>)
// ...
else {
using invalid_array = int[-1]; // ill-formed: invalid for every T
static_assert(false, "Must be arithmetic"); // ill-formed before CWG2518
}
}
До реализации CWG issue 2518 для такой универсальной инструкции обычно использовали выражение, зависимое от типа и всегда дающее template<typename>
constexpr bool dependent_false_v = false;
template<typename T>
void f()
{
if constexpr (std::is_arithmetic_v<T>)
// ...
else {
// workaround before CWG2518
static_assert(dependent_false_v<T>, "Must be arithmetic");
}
}
Объявление typedef или объявление псевдонима(начиная с C++23) можно использовать в качестве init-statement инструкции constexpr if, чтобы ограничить область видимости псевдонима типа. | (начиная с C++17) |
Consteval ifИнструкция, начинающаяся с ifconsteval, называется инструкцией consteval if. Все подчиненные инструкции consteval if являются инструкциями с ограниченным потоком управления. statement должна быть составной инструкцией; даже если это не составная инструкция, она все равно будет рассматриваться как часть инструкции consteval if (и, следовательно, приведет к ошибке компиляции): constexpr void f(bool b)
{
if (true)
if consteval {}
else ; // error: not a compound-statement
// else not associated with outer if
}
Если инструкция consteval if вычисляется в контексте, явно вычисляемом во время компиляции, выполняется compound-statement. В противном случае выполняется statement, если она присутствует. Если инструкция начинается с if!consteval, и compound-statement, и statement (если она есть) должны быть составными инструкциями. Такие инструкции не считаются инструкциями consteval if, но эквивалентны им:
if consteval {} else {/* stmt */}.
if consteval {/* stmt-2 */} else {/* stmt-1 */}. compound-statement в инструкции consteval if (или statement в отрицательной форме) находится в контексте непосредственной функции, в котором вызов непосредственной функции не обязан быть константным выражением. #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 // use a compile-time friendly algorithm
{
return ipow_ct(base, exp);
}
else // use runtime evaluation
{
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 is no longer in scope
то же самое, что
if (x)
{
int i;
}
// i is no longer in scope
Область видимости имени, введенного в condition, если это объявление, охватывает тела обеих инструкций:
if (int x = f())
{
int x; // error: redeclaration of x
}
else
{
int x; // error: redeclaration of x
}
Если управление переходит к statement-true с помощью goto или longjmp, condition не вычисляется, а statement-false не выполняется.
| Встроенные преобразования в condition инструкции 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 | Применен к | Опубликованное поведение | Корректное поведение |
|---|---|---|---|
| CWG 631 | C++98 | поведение потока управления не было определено, если первая подчиненная инструкция достигалась через метку | условие не вычисляется, а вторая подчиненная инструкция не выполняется (как и в C) |
См. также
|
(C++20) | определяет, происходит ли вызов в контексте вычисления константного выражения (функция) |
| Документация по C для if statement | |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/if