Пространства имён
Варианты
Действия

Оператор if

Материал из cppreference.com
< cpp‎ | language
 
 
 
Инструкции
Метки
метка : оператор
Операторы выражений
выражение ;
Составные операторы
{ оператор... }
Операторы выбора
if
switch
Операторы итерирования
while
do-while
for
диапазонный for(C++11)
Операторы переходов
break
continue
return
goto
Операторы объявления
объявление ;
Блоки try
try составной-операторпоследовательность-обработчиков
Транзакционная память
synchronized, atomic_commit, и т.д. (ТС TM)
 

Условно выполняет другой оператор.

Используется там, где код должен быть выполнен на основе условия времени выполнения или времени компиляции(начиная с C++17), или независимо от того, вычисляется ли оператор if в контексте с явно вычисляемой константой.(начиная с C++23).

Содержание

[править]Синтаксис

атрибуты (необязательно)ifconstexpr(необязательно)(оператор-инициализации (необязательно)условие)оператор-true (1)
атрибуты (необязательно)ifconstexpr(необязательно)(оператор-инициализации (необязательно)условие)оператор-trueelseоператор-false (2)
атрибуты (необязательно)if!(необязательно)constevalсоставной-оператор (3) (начиная с C++23)
атрибуты (необязательно)if!(необязательно)constevalсоставной-операторelseоператор (4) (начиная с C++23)
1)оператор if без ветки else
2)оператор if с веткой else
3)consteval оператор if без ветки else
4)consteval оператор if с веткой else
атрибуты(начиная с C++11) любое количество атрибутов
constexpr(начиная с C++17) если присутствует, оператор становится constexpr if оператором
оператор-инициализации(начиная с C++17) одно из следующего
(начиная с C++23)
Обратите внимание, что любой оператор-инициализации должен заканчиваться точкой с запятой ;, поэтому его часто неофициально описывают как выражение или объявление, за которым следует точка с запятой.
условие одно из
утверждение-истинно любой оператор (часто составной оператор), который выполняется, если условие оценивается как 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 эквивалентен

{
оператор-инициализации
атрибуты (необязательно)ifconstexpr(необязательно)(условие)
утверждение-истинно

}

или

{
оператор-инициализации
атрибуты (необязательно)ifconstexpr(необязательно)(условие)
утверждение-истинно
else
утверждение-ложно

}

За исключением того, что имена, объявленные оператором инициализации (если оператор инициализации является объявлением), и имена, объявленные условием (если условие является объявлением), находятся в одной и той же области видимости, которая также является областью видимости обоих операторов.

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

Оператор, начинающийся с if constexpr известен как оператор 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 цели, метки case и default:), появляющиеся в подоператоре constexpr, если на них можно сослаться (с помощью switch или goto) только в том же подоператоре.

Примечание: объявление typedefили объявление псевдонима(начиная с C++23) можно использовать в качестве оператора инициализации оператора constexpr if, чтобы уменьшить область видимости псевдонима типа.

(начиная с C++17)

Consteval if

Оператор, начинающийся с if consteval , известен как оператор consteval if. В операторе consteval if,

атрибуты (необязательно)if!(необязательно)constevalсоставной-оператор (3) (начиная с C++23)
атрибуты (необязательно)if!(необязательно)constevalсоставной-операторelseоператор (4) (начиная с C++23)

и составной-оператор и оператор (если есть) должны быть составными операторами.

Если оператор не является составным оператором, он всё равно будет рассматриваться как часть оператора consteval if (и, таким образом, приведёт к ошибке компиляции):

constexprvoid f(bool b){if(true)if consteval {}else;// ошибка: не составной оператор// else не связан с внешним if}

Если оператор consteval if оценивается в явно оцениваемом константном контексте, выполняется составной-оператор. Иначе, оператор выполняется, если он присутствует.

Метка case или default , появляющаяся в операторе consteval if, должна быть связана с оператором switch в том же операторе if. На метку, объявленную в подоператоре оператора consteval if, должна ссылаться инструкция только в том же подоператоре.

Если оператор начинается с if !consteval, составной-оператор и оператор (если есть) должны оба быть составными операторами. Такие операторы не считаются операторами consteval if, но эквивалентны операторам consteval if:

  • if!consteval {/*stmt*/} эквивалентно if consteval {}else{/*stmt*/}.
  • if!consteval {/*stmt-1*/}else{/*stmt-2*/} эквивалентно if consteval {/*stmt-2*/}else{/*stmt-1*/}.

составной-оператор в операторе 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)

switch и goto не могут переходить в ветку утверждения constexpr if или утверждения consteval if(начиная с C++23).

(начиная с C++17)
Макрос тест функциональностиЗначениеСтандартКомментарий
__cpp_if_constexpr201606L(C++17)constexprif
__cpp_if_consteval202106L(C++23)constevalif

[править]Ключевые слова

if, else, constexpr, consteval

[править]Отчёты о дефектах

Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:

Номер Применён Поведение в стандарте Корректное поведение
CWG 631 C++98 поток управления не был указан, если первый подоператор
достигается через метку
условие не оценивается и второй подоператор не
выполняется (так же, как в C)

[править]Смотрите также

определяет, происходит ли вызов в контексте вычисления константы
(функция)[править]
Документация C по Оператор if
close