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 文が明示的に定数評価されるコンテキストで評価されるかどうか (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) else ブランチを持たない consteval if 文
4) else ブランチを持つ consteval if 文
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 または simple declaration のいずれかです。

  • 構文的に structured binding 宣言として解決できる場合、それはstructured binding宣言として解釈されます。
(C++26以降)
  • 構文的に式として解決できる場合は、式として扱われます。それ以外の場合は、構造化束縛宣言ではない宣言として扱われます (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以降)

宣言の決定変数は、宣言された変数です。

Structured binding 宣言

この宣言には以下の制限があります:

この宣言の決定変数は、宣言によって導入される発明された変数 e です

(C++26以降)

ブランチ選択

condition true を返す場合、 statement-true が実行されます。

else 部分が存在し、 condition false を返す場合、 statement-false が実行されます。

else 部分が存在し、かつ statement-true if 文である場合、その内側の if 文も同様に else 部分を含まなければならない(言い換えると、ネストされた if 文において、 else は、まだ関連付けられていない else を持たない最も近い if に関連付けられる)。

#include <iostream>
int main()
{
    // else節を持つ単純なif文
    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  (optional) if constexpr (optional) ( condition )
statement-true

}

または

{
init-statement
attr  (optional) if constexpr (optional) ( 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 文 として知られています。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; // T = int* の場合、戻り値の型は int と推論される
    else
        return t;  // T = int の場合、戻り値の型は int と推論される
}

破棄された文は、定義されていない変数を ODR-use する可能性があります:

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 文が テンプレート化されたエンティティ 内に現れ、かつ condition がインスタンス化後に 値依存 でない場合、外側のテンプレートがインスタンス化されるときに破棄された文はインスタンス化されません。

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以降) は、型エイリアスのスコープを縮小するために、constexpr if文の init-statement として使用できます。

(C++17以降)


Consteval if

if consteval で始まる文は、 consteval if 文 として知られています。consteval if 文のすべての部分文は 制御フロー限定文 です。

statement は複合文でなければならず、複合文でない場合でも(そのためコンパイルエラーが発生します)、依然としてconsteval if文の一部として扱われます:

constexpr void f(bool b)
{
    if (true)
        if consteval {}
        else ; // エラー: 複合文ではない
               // else は外側の if に関連付けられていない
}

consteval if 文が 明示的に定数評価されるコンテキスト で評価される場合、 compound-statement が実行されます。そうでない場合、 statement が存在すればそれが実行されます。

文が 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 // コンパイル時フレンドリーなアルゴリズムを使用
    {
        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 適用対象 公開時の動作 正しい動作
CWG 631 C++98 最初の副文がラベルを経由して到達された場合の
制御フローは未規定であった
条件は評価されず、2番目の副文は
実行されない(C言語と同じ)

関連項目

定数評価コンテキスト内での呼び出しかどうかを検出する
(関数)