命名空間
變體
動作

if 陳述式

出自 cppreference.com
< cpp‎ | language
 
 
C++ 語言
一般主題
流程控制
條件執行陳述式
if
疊代陳述式 (迴圈)
for
範圍 for (C++11)
跳躍陳述式
函式
函式宣告
Lambda 函式運算式
inline 指定符
動態例外規範 (直到 C++17*)
noexcept 指定符 (C++11)
例外
命名空間
型別
指定符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
儲存期指定符
初始化
 
 

條件式地執行另一個陳述式。

用於需要根據條件執行程式碼的情況,或者根據 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 起)
1) 沒有 else 分支的 if 陳述式
2) 具有 else 分支的 if 陳述式
3) 沒有 else 分支的 consteval if 陳述式
4) 具有 else 分支的 consteval if 陳述式
屬性 - (C++11 起) 任意數量的 屬性 (attributes)
constexpr - (C++17 起) 若存在,該陳述式變為 constexpr if 陳述式
初始化陳述式 (init-statement) - (C++17 起) 可以是
(自 C++23 起)

請注意,任何 init-statement 都必須以分號結尾。這就是為什麼它通常被非正式地描述為一個表達式或宣告,後面跟著一個分號。

條件 (condition) - 一個 條件
為真的陳述式 (statement-true) - condition 的結果為 true 時所執行的 陳述式
為假的陳述式 (statement-false) - condition 的結果為 false 時所執行的陳述式
compound-statement - if 陳述式在 顯式常數評估上下文 中被評估(或者若 !consteval 前面,則是在非此類上下文中),則執行該 複合陳述式
statement - if 陳述式未在顯式常數評估上下文中被評估(或者若 !consteval 前面,則是在此類上下文中),則執行該陳述式(必須是複合陳述式,參見下方

[編輯] 條件

condition 可以是 表達式簡單宣告

  • 如果它在語法上可以被解析為 結構化綁定 宣告,則將其解釋為結構化綁定宣告。
(C++26 起)
  • 如果它在語法上可以被解析為表達式,則將其視為表達式。否則,將其視為宣告(非結構化綁定宣告)(C++26 起)

當控制流到達條件時,條件會產生一個值,該值用於決定控制流將前往哪個分支。

[編輯] 表達式

如果 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 起)

[編輯] 分支選擇

如果 condition 的結果為 true,則執行 statement-true

如果 if 陳述式的 else 部分存在且 condition 的結果為 false,則執行 statement-false

如果 if 陳述式的 else 部分存在且 statement-true 也是一個 if 陳述式,則該內部的 if 陳述式也必須包含 else 部分(換句話說,在巢狀的 if 陳述式中,else 關聯到最近的尚未有關聯 elseif)。

#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)

}

或者

{
初始化陳述式 (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 陳述式中,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-statementstatement(若有)都必須是複合陳述式。此類陳述式不被視為 consteval if 陳述式,但等同於 consteval if 陳述式

  • if !consteval {/* stmt */ } 等同於
if consteval {} else {/* stmt */}.
  • if !consteval {/* stmt-1 */} else {/* stmt-2 */} 等同於
if consteval {/* stmt-2 */} else {/* stmt-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-truestatement-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
}

若透過 gotolongjmp 進入 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 文件 關於 if 陳述式
English Deutsch 日本語 中文(简体) 中文(繁體)