Spec-Zone.ru › C++

if инструкция

Условно выполняет другую инструкцию.

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

Синтаксис

attr (необязательно) if constexpr(необязательно)
( init-statement (необязательно) condition ) statement-true
(1)
attr (необязательно) if constexpr(необязательно)
( init-statement (необязательно) condition ) statement-true else statement-false
(2)
attr (необязательно) if !(необязательно) consteval compound-statement (3) (начиная с C++23)
attr (необязательно) if !(необязательно) consteval compound-statement else statement (4) (начиная с C++23)
1) инструкция if без ветви else
2) инструкция if с ветвью else
3) инструкция consteval if без ветви else
4) инструкция consteval if с ветвью else
attr - (начиная с C++11) любое количество атрибутов
constexpr - (начиная с C++17) если присутствует, инструкция становится инструкцией constexpr if
init-statement - (начиная с C++17) одно из следующего:
  • инструкция-выражение (которая может быть пустой инструкцией ;)
  • простое объявление, обычно объявление переменной с инициализатором, но оно может объявлять любое количество переменных или быть объявлением структурированной привязки
  • объявление псевдонима
(начиная с C++23)

Обратите внимание, что любой init-statement должен заканчиваться точкой с запятой. Поэтому его часто неформально описывают как выражение или объявление, за которым следует точка с запятой.

condition - условие
statement-true - инструкция, выполняемая, если condition дает true
statement-false - инструкция, выполняемая, если condition дает false
compound-statement - составная инструкция, выполняемая, если инструкция if вычисляется в контексте, явно вычисляемом во время компиляции (или не вычисляется в таком контексте, если перед consteval стоит !)
statement - инструкция (должна быть составной инструкцией, см. ниже), выполняемая, если инструкция if не вычисляется в контексте, явно вычисляемом во время компиляции (или вычисляется в таком контексте, если перед consteval стоит !)

Условие

condition может быть выражением (expression) или простым объявлением.

  • Если его можно синтаксически разобрать как объявление структурированной привязки, оно интерпретируется как такое объявление.
(начиная с C++26)
  • Если его можно синтаксически разобрать как выражение, оно рассматривается как выражение. В противном случае оно рассматривается как объявление, не являющееся объявлением структурированной привязки(начиная с C++26).

При достижении условия оно дает значение, которое используется для определения того, в какую ветвь перейдет управление.

Выражение

Если condition является выражением, полученное им значение — это значение выражения, контекстно преобразованное в bool. Если такое преобразование некорректно, программа некорректна.

Объявление

Если condition является простым объявлением, полученное им значение — это значение переменной выбора (см. ниже), контекстно преобразованное в bool. Если такое преобразование некорректно, программа некорректна.

Объявление, не являющееся объявлением структурированной привязки

Объявление должно удовлетворять следующим ограничениям:

  • Синтаксически соответствует следующей форме:
  • type-specifier-seq declarator = assignment-expression
(до C++11)
  • attribute-specifier-seq(необязательно) decl-specifier-seq declarator brace-or-equal-initializer
(начиная с C++11)
  • Декларатор не может указывать на функцию или массив.
  • последовательность спецификаторов типа(до C++11)последовательность спецификаторов объявления может содержать только спецификаторы типа и constexpr, а также(начиная с C++11) не может определять класс или перечисление.

Переменная выбора этого объявления — объявленная переменная.

Объявление структурированной привязки

Объявление должно удовлетворять следующим ограничениям:

  • expression в его инициализаторе не может иметь тип массива.
  • Последовательность спецификаторов объявления может содержать только спецификаторы типа и constexpr.

Переменная выбора этого объявления — вымышленная переменная e, вводимая объявлением.

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

Выбор ветви

Если condition дает true, выполняется statement-true.

Если часть else инструкции if присутствует и condition дает false, выполняется statement-false.

Если часть else инструкции if присутствует, а statement-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 с инициализатором

Если используется init-statement, инструкция if эквивалентна

{
init-statement
attr (необязательно) if constexpr(необязательно) ( condition )
statement-true

}

или

{
init-statement
attr (необязательно) if constexpr(необязательно) ( condition )
statement-true
else statement-false

}

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

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)

Constexpr if

Инструкция, начинающаяся с ifconstexpr, называется инструкцией constexpr if. Все подчиненные инструкции constexpr if являются инструкциями с ограниченным потоком управления.

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

Если condition дает 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;
}

Вне шаблона отброшенная инструкция полностью проверяется. ifconstexpr не заменяет директиву препроцессора #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
    }
}

До реализации CWG issue 2518 для такой универсальной инструкции обычно использовали выражение, зависимое от типа и всегда дающее false:

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

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

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

Consteval if

Инструкция, начинающаяся с ifconsteval, называется инструкцией consteval if. Все подчиненные инструкции consteval if являются инструкциями с ограниченным потоком управления.

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

Если инструкция начинается с if!consteval, и compound-statement, и statement (если она есть) должны быть составными инструкциями. Такие инструкции не считаются инструкциями 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)
Макрос проверки поддержки возможностей Значение Стандарт Возможность
__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 statement

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

Spec-Zone.ru

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