名前空間
変種
操作

if

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
その他
 
 

他の文を条件付きで実行します。

条件に基づいてコードを実行する必要がある場合、または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) else分岐のないif
2) else分岐のあるif
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 - conditiontrueを生成した場合に実行される
statement-false - conditionfalseを生成した場合に実行される文
compound-statement - if文が定数評価が明らかな文脈で評価された場合に実行される複合文constevalの前に!がある場合は、そのような文脈で評価されない場合)
statement - if文が定数評価が明らかな文脈で評価されない場合に実行される文(複合文でなければならない、下記参照)(constevalの前に!がある場合は、そのような文脈で評価される場合)

[編集] 条件

conditionまたは単純宣言のいずれかです。

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

制御がconditionに達すると、conditionは値を生成し、その値は制御がどの分岐に進むかを決定するために使用されます。

[編集]

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以降)

[編集] 分岐の選択

conditiontrueを生成した場合、statement-trueが実行されます。

if文のelse部分が存在し、conditionfalseを生成した場合、statement-falseが実行されます。

if文のelse部分が存在し、statement-trueif文である場合、その内側のif文もelse部分を含まなければなりません(言い換えると、ネストしたif文では、elseは、まだ関連付けられたelseを持たない最も近いifに関連付けられます)。

#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

}

or

{
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文として知られています。constexpr if文のすべてのサブ文は、制御フローが制限された文です。

constexpr if文では、conditionbool型の文脈的に変換された定数式(C++23まで)boolに文脈的に変換される式でなければならず、その変換は定数式(C++23以降)でなければなりません。

conditiontrueを生成する場合、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-useすることができます

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

このような包括的な文に対して、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以降)を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 ; // error: not a compound-statement
               // else not associated with outer if
}

consteval if文が定数評価が明らかな文脈で評価された場合、compound-statementが実行されます。そうでない場合、statementが存在すればそれが実行されます。

文がif !constevalで始まる場合、compound-statementstatement(もしあれば)は両方とも複合文でなければなりません。そのような文はconsteval if文とは見なされませんが、consteval if文と等価です

  • if !consteval {/* 文 */ }は以下と等価です
if consteval {} else {/* 文 */}.
  • if !consteval {/* 文1 */} else {/* 文2 */}は以下と等価です
if consteval {/* 文2 */} else {/* 文1 */}.

consteval if文のcompound-statement(または否定形の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-truegotoまたはlongjmpによって入力された場合、conditionは評価されず、statement-falseは実行されません。

constexpr if文のconditionでは、縮小変換でない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と同様)
サブ文は実行されない(Cと同様)

[編集] 関連項目

呼び出しが定数評価文脈内で発生したかどうかを検出する
(関数) [編集]
English 日本語 中文(简体) 中文(繁體)