名前空間
変種
操作

if 文

提供: cppreference.com
< cpp‎ | language
 
 
C++言語
一般的なトピック
フロー制御
条件付き実行文
if
繰り返し文 (ループ)
ジャンプ文
関数
関数宣言
ラムダ関数宣言
inline 指定子
例外指定 (非推奨)
noexcept 指定子 (C++11)
例外
名前空間
指定子
decltype (C++11)
auto (C++11)
alignas (C++11)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点
文字 - 文字列 - nullptr (C++11)
ユーザ定義 (C++11)
ユーティリティ
属性 (C++11)
typedef 宣言
型エイリアス宣言 (C++11)
キャスト
暗黙の変換 - 明示的な変換
static_cast - dynamic_cast
const_cast - reinterpret_cast
メモリ確保
クラス
クラス固有の関数特性
特別なメンバ関数
テンプレート
その他
 
ラベル
label : statement
式文
expression ;
複合文
{ statement... }
選択文
if
switch
繰り返し文
while
do-while
for
範囲 for(C++11)
ジャンプ文
break
continue
return
goto
宣言文
declaration ;
try ブロック
try compound-statement handler-sequence
トランザクショナルメモリ
synchronized, atomic_commit など(TM TS)
 

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

実行時またはコンパイル時の状況に基づいてコードを実行する必要があるときに使用されます。

目次

[編集] 構文

attr(オプション) if ( condition ) statement-true (C++17以前)
attr(オプション) if ( condition ) statement-true else statement-false (C++17以前)
attr(オプション) if constexpr(オプション) ( init-statement(オプション) condition ) statement-true (C++17およびそれ以降)
attr(オプション) if constexpr(オプション) ( init-statement(オプション) condition ) statement-true else statement-false (C++17およびそれ以降)
attr(C++11) - 任意の個数の属性
condition - 以下のいずれか。
init-statement(C++17) - 以下のいずれか。
  • 式文 (空文;」でも構いません)。
  • 単純宣言。 一般的には初期化子付きの1個の変数の宣言ですが、好きなだけ多くの変数を宣言して構いませんし、分解宣言であっても構いません。
あらゆる init-statement はセミコロン ; で終わらなければならないことに注意してください。 これが式または宣言にセミコロンが続いたものとしてしばしば非形式的に説明される理由です。
statement-true - 任意の (しばしば複合文)。

conditiontrue に評価された場合に実行されます。

statement-false - 任意の (しばしば複合文)。 conditionfalse に評価された場合に実行されます。

[編集] 説明

conditionbool への変換後に true を生成した場合は、 statement-true が実行されます。

if 文の else 部が存在し、 conditionbool への変換後に false を生成した場合は、 statement-false が実行されます。

if 文の2つめの形式 (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 (i > 1) のではなく if (j > 2) の一部です。
            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
if constexpr(オプション) ( condition )
statement-true

}

または

{
init-statement
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; // mx によって保護される
int demo() {
   if (auto it = m.find(10); it != m.end()) { return it->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 (auto keywords = {"if", "for", "while"};
       std::any_of(keywords.begin(), keywords.end(),
                   [&s](const char* kw) { return s == kw; })) {
     std::cerr << "Token must not be a keyword\n");
   }
}
(C++17およびそれ以降)

constexpr if

if constexpr で始まる文は constexpr if 文と呼ばれます。

constexpr if 文では、 condition は文脈的に変換された bool 型の定数式でなければなりません。 その値が 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 使用できます。

extern int x; // x の定義は要求されません。
int f() {
if constexpr (true)
    return 0;
else if (x)
    return x;
else
    return -x;
}

constexpr if 文がテンプレート化されたエンティティの内部に現れた場合、かつ、 condition が実体化後に値依存でない場合、囲っているテンプレートが実体化されたとき、破棄された式は実体化されません。

template<typename T, typename ... Rest>
void g(T&& p, Rest&& ...rs) {
    // p を使って何か処理する
    if constexpr (sizeof...(rs) > 0)
        g(rs...); // 空の引数リストで実体化されることはありません。
}

テンプレートの外側では、破棄された文は完全にチェックされます。 if constexpr #if プリプロセッサ指令の置き換えではありません。

void f() {
    if constexpr(false) {
        int i = 0;
        int *p = i; // 破棄された文内であってもエラー
    }
}


ノート: 実体化後も条件式に値依存が残る例はネストしたテンプレートです。 例えば、

template<class T> void g() {
    auto lm = [](auto p) {
        if constexpr (sizeof(T) == 1 && sizeof p == 1) {
           // この条件式は g<T> の実体化後も値依存が残ります。
        }
    };
}

ノート: 破棄された文は、すべての有り得る特殊化について ill-formed であってはなりません。

template <typename T>
void f() {
     if constexpr (std::is_arithmetic_v<T>)
         // ...
     else
       static_assert(false, "Must be arithmetic"); // ill-formed、すべての T について無効
}

そのような全キャッチ文に対するワークアラウンドは、常に false になる型依存の式です。

template<class T> struct dependent_false : std::false_type {};
template <typename T>
void f() {
     if constexpr (std::is_arithmetic_v<T>)
         // ...
     else
       static_assert(dependent_false<T>::value, "Must be arithmetic"); // OK
}

constexpr if の部分文内に現れるラベル (goto の飛び先case ラベル、および default:) は、同じ部分文内 (の switch または goto) からのみ参照できます。

(C++17およびそれ以降)

[編集] ノート

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-truegoto または longjmp によって入られた場合、 statement-false は実行されません。

(C++14およびそれ以降)

switch および goto が constexpr if 文の分岐内にジャンプすることは許されません。

(C++17およびそれ以降)

[編集] キーワード

if, else, constexpr

[編集] 関連項目