Оператор if
Условно выполняет другой оператор.
Используется там, где код должен быть выполнен на основе условия времени выполнения или времени компиляции(начиная с C++17), или независимо от того, вычисляется ли оператор if в контексте с явно вычисляемой константой.(начиная с C++23).
Содержание |
[править]Синтаксис
атрибуты (необязательно)if constexpr (необязательно)( оператор-инициализации (необязательно)условие) оператор-true | (1) | ||||||||
атрибуты (необязательно)if constexpr (необязательно)( оператор-инициализации (необязательно)условие) оператор-trueelse оператор-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 с оператором elseint i =2;if(i >2)std::cout<< i <<" больше чем 2\n";elsestd::cout<< i <<" не больше чем 2\n"; // вложенный оператор ifint 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_caststruct 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;externbool 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(constauto keywords ={"if", "for", "while"}; std::ranges::any_of(keywords, [&tok](constchar* 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){ifconstexpr(std::is_pointer_v<T>)return*t;// выводит возвращаемый тип в int для T = int*elsereturn t;// выводит возвращаемый тип в int для T = int} Отброшенный оператор может использовать odr переменную, которая не определена externint x;// определение x не требуется int f(){ifconstexpr(true)return0;elseif(x)return x;elsereturn-x;} Вне шаблона отброшенный оператор полностью проверяется. ifconstexpr не заменяет директиву предварительной обработки #if: void f(){ifconstexpr(false){int i =0;int*p = i;// Ошибка, даже если в отброшенном утверждении}} Если оператор constexpr if появляется внутри шаблонной сущности, и если условие не зависит от значения после создания экземпляра, отброшенный оператор не создаётся при создании экземпляра включающего шаблона. template<typename T, typename ... Rest>void g(T&& p, Rest&& ...rs){// ... обработка pifconstexpr(sizeof...(rs)>0) g(rs...);// никогда не создаётся с пустым списком аргументов.} Примечание. Примером, когда условие остаётся зависимым от значения после создания экземпляра, является вложенный шаблон, например template<class T>void g(){auto lm =[=](auto p){ifconstexpr(sizeof(T)==1&& sizeof p ==1){// это условие остаётся зависимым от значения после создания экземпляра g<T>,// который влияет на неявные захваты лямбда-выражений// этот составной оператор можно отбросить только после создания// экземпляра тела лямбда-выражения}};} Примечание: отброшенный оператор не может быть некорректным для каждой возможной специализации: template<typename T>void f(){ifconstexpr(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>inlineconstexprbool dependent_false_v =false; template<typename T>void f(){ifconstexpr(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 (и, таким образом, приведёт к ошибке компиляции): Запустить этот код constexprvoid f(bool b){if(true)if consteval {}else;// ошибка: не составной оператор// else не связан с внешним if} Если оператор consteval if оценивается в явно оцениваемом константном контексте, выполняется составной-оператор. Иначе, оператор выполняется, если он присутствует. Метка Если оператор начинается с
составной-оператор в операторе consteval if (или операторе в отрицательной форме) находится в контексте немедленной функции, в котором вызов немедленной функции не обязательно должен быть константным выражением. Запустить этот код #include <cmath>#include <cstdint>#include <cstring>#include <iostream> constexprbool is_constant_evaluated()noexcept{if consteval {returntrue;}else{returnfalse;}} constexprbool is_runtime_evaluated()noexcept{if not consteval {returntrue;}else{returnfalse;}} 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;} constexprstd::uint64_t ipow(std::uint64_t base, std::uint8_t exp){if consteval // использовать дружественный алгоритм времени компиляции{return ipow_ct(base, exp);}else// использовать оценку во время выполнения{returnstd::pow(base, exp);}} int main(int, constchar* 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) | constexprif |
__cpp_if_consteval | 202106L | (C++23) | constevalif |
[править]Ключевые слова
if, else, constexpr, consteval
[править]Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
Номер | Применён | Поведение в стандарте | Корректное поведение |
---|---|---|---|
CWG 631 | C++98 | поток управления не был указан, если первый подоператор достигается через метку | условие не оценивается и второй подоператор не выполняется (так же, как в C) |
[править]Смотрите также
(C++20) | определяет, происходит ли вызов в контексте вычисления константы (функция) |
Документация C по Оператор if |