Namespaces
Variants

if statement

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
if
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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

Используется там, где код должен быть выполнен на основе условия , или когда if оператор вычисляется в явно константно-вычисляемом контексте (since 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 - (since C++11) любое количество атрибутов
constexpr - (since C++17) если присутствует, оператор становится constexpr if оператором
init-statement - (since C++17) либо
(since C++23)

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

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

Условие

Условием может быть либо condition , либо expression , либо simple declaration .

  • Если синтаксически это может быть разрешено как structured binding объявление, то оно интерпретируется как объявление структурированной привязки.
(since 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)

Переменная решения объявления - это объявленная переменная.

Декларация структурированной привязки

Декларация имеет следующие ограничения:

Переменной решения декларации является введённая переменная e введённая декларацией .

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

Выбор ветки

Если условие возвращает true , выполняется statement-true .

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

Если присутствует else часть оператора if и statement-true также является оператором if , то этот внутренний оператор if также должен содержать часть else (другими словами, во вложенных операторах if , else связывается с ближайшим оператором if , который еще не имеет связанной части else ).

#include <iostream>
int main()
{
    // простой оператор if с веткой else
    int i = 2;
    if (i > 2)
        std::cout << i << " is greater than 2\n";
    else
        std::cout << i << " is not greater than 2\n";
    // вложенный оператор if
    int j = 1;
    if (i > 1)
        if (j > 2)
            std::cout << i << " > 1 and " << j << " > 2\n";
        else // этот else относится к if (j > 2), а не к if (i > 1)
            std::cout << i << " > 1 and " << 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 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

Утверждение, начинающееся с if constexpr , известно как constexpr if statement . Все подутверждения constexpr if statement являются control-flow-limited statements .

В операторе constexpr if условие должно быть контекстно преобразуемым константным выражением типа 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; // выводит тип возвращаемого значения как 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 оператор появляется внутри шаблонной сущности , и если условие не является value-dependent после инстанцирования, отброшенный оператор не инстанцируется при инстанцировании охватывающего шаблона.

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 {
        using invalid_array = int[-1]; // некорректно: недопустимо для любого T
        static_assert(false, "Must be arithmetic"); // некорректно до 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 {
        // обходное решение до CWG2518
        static_assert(dependent_false_v<T>, "Must be arithmetic");
    }
}

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

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


Consteval if

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

statement должен быть составным оператором, и он все равно будет рассматриваться как часть константного оператора if, даже если он не является составным оператором (и, следовательно, приводит к ошибке компиляции):

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

Если выражение consteval if вычисляется в контексте явного постоянного вычисления , compound-statement выполняется. В противном случае, statement выполняется, если он присутствует.

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

  • if ! consteval { /* stmt */ } эквивалентно
if consteval { } else { /* stmt */ } .
**Перевод:**
if consteval { } else { /* stmt */ } .
**Примечание:** Весь код внутри тегов ` ` сохранен без изменений, так как содержит C++ код и специфические термины. Точка в конце предложения также сохранена, так как является частью форматирования.
  • 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)

Примечания

Если statement-true или statement-false не является составным оператором, он обрабатывается как если бы был:

if (x)
    int i;
// переменная i больше не в области видимости

то же самое, что

if (x)
{
    int i;
}
// переменная i больше не в области видимости

Область видимости имени, введенного condition , если это объявление, является объединенной областью видимости тел обоих операторов:

if (int x = f())
{
    int x; // ошибка: переопределение x
}
else
{
    int x; // ошибка: переопределение x
}

Если statement-true выполняется через goto или longjmp , condition не вычисляется и statement-false не выполняется.

Встроенные преобразования не разрешены в условии 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 Applied to Behavior as published Correct behavior
CWG 631 C++98 управление потоком выполнения было неопределенным, если
первое подвыражение достигается через метку
условие не вычисляется и второе
подвыражение не выполняется (так же как в C)

Смотрите также

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