if
statement
条件付きで別の文を実行します。
条件に基づいてコードを実行する必要がある場合に使用される 、または 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以降) | |||||||
| attr | - | (C++11以降) 任意の数の 属性 | ||
constexpr
|
- | (C++17以降) 指定された場合、文は constexpr if文 となる | ||
| init-statement | - |
(C++17以降)
以下のいずれか
任意の init-statement はセミコロンで終了する必要がある。このため、非公式には「式または宣言の後にセミコロンが続くもの」と説明されることが多い。 |
||
| condition | - | 条件式 | ||
| statement-true | - | condition が true と評価された場合に実行される 文 | ||
| statement-false | - | condition が false と評価された場合に実行される文 | ||
| compound-statement | - |
if
文が
明示的に定数評価される文脈
で評価される場合に実行される
複文
(または
!
が
consteval
の前に付く場合、そのような文脈で評価されない場合)
|
||
| statement | - |
if
文が明示的に定数評価される文脈で評価されない場合に実行される文(複文である必要あり、
後述
)(または
!
が
consteval
の前に付く場合、そのような文脈で評価される場合)
|
条件
condition は、 expression または simple declaration のいずれかです。
|
(C++26以降) |
- 構文的に式として解決できる場合は、式として扱われます。それ以外の場合は、構造化束縛宣言ではない宣言として扱われます (since C++26) (C++26以降) 。
制御が条件式に到達すると、条件式は値を生成し、その値を使用して制御がどの分岐に進むかを決定します。
式
condition が式である場合、その評価結果は式が文脈的に bool に変換された値となります。この変換が不適格な場合、プログラムは不適格となります。
宣言
condition が単純な宣言である場合、その結果の値は決定変数(下記参照)の値を文脈的に bool に変換したものである。その変換が不適格な場合、プログラムは不適格となる。
非構造化バインディング宣言
この宣言には以下の制限があります:
- 構文的には以下の形式に従います:
|
(C++11まで) |
|
(C++11以降) |
- 宣言子は 関数 または 配列 を指定できません。
- 型指定子シーケンス (C++11まで) 宣言指定子シーケンス は型指定子と constexpr のみを含むことができ、 (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 によって宣言された名前( 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 ifif 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 ifif 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文と同等です:
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言語と同じ) |
関連項目
|
(C++20)
|
定数評価コンテキスト内での呼び出しかどうかを検出する
(関数) |
|
Cドキュメント
for
if
文
|
|