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

Оператор 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).

Содержание

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

атрибуты (необязательно) 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