Namespaces
Variants

if statement

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
if
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

有条件地执行另一条语句。

用于需要根据条件执行代码的情况 ,或者 if 语句是否在显式常量求值上下文中被求值 (since 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) if 语句没有 else 分支
2) if 语句带有一个 else 分支
3) 不带 else 分支的 consteval if 语句
4) 带有 else 分支的 consteval if 语句
attr - (since C++11) 任意数量的 属性
constexpr - (since C++17) 若存在,该语句将成为 constexpr if 语句
init-statement - (since C++17) 以下之一
(since C++23)

注意:任何 init-statement 必须以分号结尾。因此常被非正式地描述为表达式或声明后接分号。

condition - 一个 条件
statement-true - condition 结果为 true 时要执行的 语句
statement-false - condition 结果为 false 时要执行的语句
compound-statement - if 语句在 明显常量求值上下文 中被求值时要执行的 复合语句 (若在 consteval 前有 ! 则指不在该上下文中求值时)
statement - if 语句不在明显常量求值上下文中求值时要执行的语句(必须是复合语句,参见 下文 )(若在 consteval 前有 ! 则指在该上下文中求值时)

条件表达式

一个 条件 可以是 表达式 ,也可以是 简单声明

  • 如果语法上可以解析为 结构化绑定 声明,则将其解释为结构化绑定声明。
(since C++26)
  • 如果它能被语法解析为表达式,则被视为表达式。否则,它被视为声明 (非结构化绑定声明 (自 C++26 起)

当控制流到达条件语句时,该条件将产生一个值,该值用于确定控制流将转向哪个分支。

表达式

如果 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()
{
    // 带 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
attr  (optional) if constexpr (optional) ( condition )
statement-true

}

{
init-statement
attr  (optional) if constexpr (optional) ( condition )
statement-true
else
statement-false

}

除了由 init-statement 声明的名称(若 init-statement 是声明)和由 condition 声明的名称(若 condition 是声明)在同一作用域内,该作用域也是两个 statement s 的作用域。

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";
    }
}
(since C++17)


Constexpr if

if constexpr 开头的语句被称为 constexpr if 语句 。constexpr if 语句的所有子语句都是 控制流受限语句

在 constexpr if 语句中, 条件 必须是 上下文转换到 bool 类型的常量表达式 (C++23 前) 上下文转换到 bool 的表达式,且该转换是 常量表达式 (C++23 起)

如果 条件 产生 true ,则 false 语句 被丢弃(如果存在),否则 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 使用 未定义的变量:

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 语句出现在 模板化实体 内部,且实例化后 条件 不是 值依赖的 ,则当外围模板实例化时不会实例化被丢弃语句。

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> 实例化后保持值依赖,
            // 这会影响隐式 lambda 捕获
            // 该复合语句可能仅在 lambda 体实例化后被丢弃
        }
    };
}

被丢弃语句不能对所有可能的特化都非良构:

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 问题 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 语句的 初始化语句 以缩小类型别名的作用域。

(C++17 起)


立即求值 if 语句

if consteval 开头的语句被称为 立即求值 if 语句 。立即求值 if 语句的所有子语句都是 控制流受限语句

语句 必须是复合语句,即使它不是复合语句(因此会导致编译错误),它仍将被视为立即求值 if 语句的一部分:

constexpr void f(bool b)
{
    if (true)
        if consteval {}
        else ; // 错误:不是复合语句
               // else 不与外层 if 关联
}

如果立即求值 if 语句在 明显常量求值上下文 中被求值,则执行 复合语句 。否则,如果存在 语句 ,则执行该语句。

如果语句以 if ! consteval 开头,则 复合语句 语句 (如果有)都必须为复合语句。此类语句不被视为立即求值 if 语句,但等价于立即求值 if 语句:

  • if ! consteval { /* stmt */ } 等价于
if consteval { } else { /* stmt */ }
  • if ! consteval { /* stmt-1 */ } else { /* stmt-2 */ } 等价于
if consteval { /* stmt-2 */ } else { /* stmt-1 */ }

立即求值 if 语句中的 复合语句 (或否定形式中的 语句 )处于 立即函数上下文 中,其中对立即函数的调用不必是常量表达式。

#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 的重声明
}

如果通过 goto longjmp 进入 statement-true ,则不会对 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++标准。

缺陷报告 应用于 发布时的行为 正确行为
CWG 631 C++98 若通过标签进入第一条子语句
控制流是未指定的
不计算条件且不执行第二条
子语句(与C语言相同)

参见

检测函数调用是否发生在常量求值上下文中
(函数)
C 文档 关于 if 语句