if 陳述式
條件式地執行另一個陳述式。
用於需要根據條件執行程式碼的情況,或者根據 if 陳述式是否在顯式常數評估上下文 (manifestly constant-evaluated context) 中進行評估(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 起) | |||||||
| 屬性 | - | (C++11 起) 任意數量的 屬性 (attributes) | ||
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 是表達式,其產生的值是該表達式在上下文中轉換為 bool 的值。如果該轉換格式錯誤,則程式格式錯誤。
[編輯] 宣告
如果 condition 是簡單宣告,其產生的值是決策變數(見下文)在上下文中轉換為 bool 的值。如果該轉換格式錯誤,則程式格式錯誤。
[編輯] 非結構化綁定宣告
該宣告具有以下限制
- 語法上符合以下形式
|
(直到 C++11) |
|
(C++11 起) |
- 宣告子 (declarator) 不能指定 函式 或 陣列。
- 型別說明符序列(C++11 前)宣告說明符序列 只能包含型別說明符和 constexpr,並且它(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()
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 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; // deduces return type to int for T = int* else return t; // deduces return type to int for T = int } 被捨棄的陳述式可以 ODR-使用一個未定義的變數 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 在具現化後不是 值依賴 (value-dependent) 的,則在具現化封裝模板時,被捨棄的陳述式不會被具現化。 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 問題 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-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 }
若透過 goto 或 longjmp 進入 statement-true,則不評估 condition,也不執行 statement-false。
|
在 constexpr if 陳述式的 condition 中不允許內建轉換,除非是轉換為 bool 的非縮窄 (narrowing) 整數轉換。 |
(自 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 | 若透過標籤到達第一個子陳述式,則控制流是不確定的 透過標籤到達第一個子陳述式 |
不評估條件,且不執行第二個子陳述式(與 C 語言相同) 不執行子陳述式(與 C 語言相同) |
[編輯] 參見
| (C++20) |
偵測呼叫是否發生在常數評估上下文中 (函式) |
| C 文件 關於 if 陳述式
| |