名稱空間
變體
操作

if 語句

來自 cppreference.com
< cpp‎ | 語言
 
 
C++ 語言
 
 

根據條件有選擇地執行其他語句。

當代碼需要根據條件執行時使用,或者在明確的常量求值上下文中對 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 起)
1) 沒有 else 分支的 if 語句
2) 帶有 else 分支的 if 語句
3) 沒有 else 分支的 consteval if 語句
4) 帶有 else 分支的 consteval if 語句
屬性 - (自 C++11 起) 任意數量的屬性
constexpr - (C++17 起) 如果存在,該語句成為 constexpr if 語句
init-statement - (C++17 起) 可以是
(C++23 起)

注意,任何 init-statement 都必須以分號結尾。這就是為什麼它通常被非正式地描述為表示式或聲明後跟分號的原因。

條件 - 一個條件
statement-true - 如果 condition 產生 true,則執行的語句
statement-false - 如果 condition 產生 false,則執行的語句
複合語句 - 如果 if 語句在明確的常量求值上下文中求值 (如果 consteval 前有 ! 則不在這樣的上下文中求值),則執行的複合語句
語句 - 如果 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 不是值依賴的,則在例項化封閉模板時,被丟棄的語句不會被例項化。

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

(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++ 標準。

缺陷報告 應用於 釋出時的行為 正確的行為
CWG 631 C++98 如果透過標籤到達第一個子語句,則控制流未指定
不評估條件,不執行第二個子語句(與 C 中相同)
不評估條件,不執行第二個子語句(與 C 中相同)
不評估條件,不執行第二個子語句(與 C 中相同)

[編輯] 參閱

檢測呼叫是否發生在常量求值上下文內
(函式) [編輯]
if 語句C 文件