Spec-Zone.ru › C++

Оператор if

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

Используется там, где код нужно выполнить на основе условия выполненияили времени компиляции(с 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)
Обратите внимание, что любой инициализирующий оператор должен заканчиваться точкой с запятой ;, поэтому он часто описывается неформально как выражение или объявление, за которым следует точка с запятой.
условие - одно из
  • выражение, которое явным образом преобразуется в bool
  • объявление одной не массивной переменной с инициализатором в фигурных скобках или равным знаком.
оператор_true - любой оператор (часто составной оператор), который выполняется, если условие вычисляется как true
оператор_false - любой оператор (часто составной оператор), который выполняется, если условие вычисляется как false
составной оператор - любой составной оператор, который выполняется, если оператор if
  • оценивается в контексте явно константного вычисления, если ! не предшествует consteval
  • не оценивается в контексте явно константного вычисления, если ! предшествует consteval
оператор - любой оператор (должен быть составным оператором, см. ниже), который выполняется, если оператор if
  • не оценивается в контексте явно константного вычисления, если ! не предшествует consteval
  • оценивается в контексте явно константного вычисления, если ! предшествует consteval

Описание

Если условие дает результат true после преобразования к типу bool, выполняется оператор_true.

Если часть else оператора if присутствует и условие дает результат false после преобразования к типу bool, выполняется оператор_false.

Второй форме оператора if (той, которая включает else), если оператор_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 с инициализатором

Если используется инициализирующий оператор, оператор if эквивалентен

{
инициализирующий_оператор
атрибут (необязательно) if constexpr(необязательно) ( условие )
оператор_true

}

или

{
инициализирующий_оператор
атрибут (необязательно) if constexpr(необязательно) ( условие )
оператор_true
else оператор_false

}

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

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)
END_OF_DOCUMENT_MARKER

Constexpr if

Утверждение, которое начинается с if constexpr известно как утверждение constexpr if.

В утверждении constexpr if значение condition должно быть контекстно преобразуемым константным выражением типа bool(до C++23)выражением, контекстно преобразуемым в bool(с C++23), где преобразование является константным выражением.

Если значение равно true, то statement-false отбрасывается (если присутствует), в противном случае — statement-true.

Операторы 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;
}

За пределами шаблона отброшенный оператор полностью проверяется. if constexpr не является заменой директиве предварительной обработки #if:

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
    }
}

Общие обходные пути до реализации CWG2518 для такого универсального оператора — это зависящее от типа выражение, которое всегда false:

template<typename>
inline 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");
    }
}

Метки (метки перехода, case метки и default:) в подвыражении constexpr if могут быть ссылаемы только (с помощью switch или goto) в том же подвыражении.

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

(с C++17)

Consteval if

Утверждение, начинающееся с if consteval известно как утверждение consteval if. В утверждении consteval if,

attr (необязательно) if !(необязательно) consteval compound-statement (3) (с C++23)
attr (необязательно) if !(необязательно) consteval compound-statement else statement (4) (с C++23)

и compound-statement, и statement (если таковое имеется) должны быть составными операторами.

Если 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, если оно присутствует.

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

Если оператор начинается с if !consteval, compound-statement и statement (если таковое имеется) должны быть составными операторами. Такие операторы не считаются операторами 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*/}.

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)

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

(с C++17)
Макрос проверки функций Значение Std Функция
__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)

См. также

is_constant_evaluated
(C++20)
определяет, происходит ли вызов в контексте вычисления константы
(функция)
Документация C для оператора if

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/if

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API