if文
他の文を条件付きで実行します。
条件に基づいてコードを実行する必要がある場合、または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の前に!がある場合は、そのような文脈で評価される場合) |
[編集] 条件
|
(C++26以降) |
- 構文的に式として解決できる場合、式として扱われます。それ以外の場合は、宣言(構造化束縛宣言ではない)(C++26以降)として扱われます。
制御がconditionに達すると、conditionは値を生成し、その値は制御がどの分岐に進むかを決定するために使用されます。
[編集] 式
conditionが式である場合、それが生成する値は、式をboolに文脈的に変換した値です。その変換が不適格である場合、プログラムは不適格となります。
[編集] 宣言
conditionが単純宣言である場合、それが生成する値は、判定変数(下記参照)の値をboolに文脈的に変換した値です。その変換が不適格である場合、プログラムは不適格となります。
[編集] 非構造化束縛宣言
宣言には以下の制限があります
- 構文的に以下の形式に準拠します
|
(C++11まで) |
|
(C++11以降) |
宣言の判定変数は、宣言された変数です。
構造化束縛宣言宣言には以下の制限があります
宣言の判定変数は、宣言によって導入される発明された変数eです。 |
(C++26以降) |
[編集] 分岐の選択
conditionがtrueを生成した場合、statement-trueが実行されます。
if文のelse部分が存在し、conditionがfalseを生成した場合、statement-falseが実行されます。
if文のelse部分が存在し、statement-trueもif文である場合、その内側の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()
初期化子付き
|
|||||||||||||||||||||||||||||||||||||||||||||||
{
|
|||||||||
or
{
|
|||||||||
ただし、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"; } }
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; // 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 ifif 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-statementとstatement(もしあれば)は両方とも複合文でなければなりません。そのような文はconsteval if文とは見なされませんが、consteval if文と等価です
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-trueがgotoまたは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と同様) |
[編集] 関連項目
| (C++20) |
呼び出しが定数評価文脈内で発生したかどうかを検出する (関数) |
| if文のC言語のドキュメント
| |