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

Оператор if

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

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

Используется там, где код должен быть выполнен на основе условия времени выполнения или времени компиляции (начиная с 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)
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 с оператором 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 эквивалентен

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

}

или

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

}

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

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

Оператор, начинающийся с if constexpr известен как оператор 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 цели, метки 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 (и, таким образом, приведёт к ошибке компиляции):

constexpr void 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>

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)

switch и goto не могут переходить в ветку утверждения constexpr if или утверждения consteval if (начиная с 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 по Оператор if