命名空间
变体
操作

if 语句

来自 cppreference.com
< cpp‎ | language
 
 
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) if 语句,没有 else 分支
2) if 语句,带有 else 分支
3) consteval if 语句,没有 else 分支
4) consteval if 语句,带有 else 分支
attr - (从 C++11 开始) 任意数量的 属性
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 之前的情况下,在该上下文中求值),则执行的语句(必须为复合语句,参见 下方)。

[edit] 条件

条件 可以是 表达式 或者 简单声明

(自 C++26 起)
  • 如果它可以语法解析为表达式或声明(它不是结构化绑定声明)(自 C++26 起),则它被解释为后者。

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

[edit] 表达式

如果 condition 是表达式,则它产生的值为表达式上下文转换为 bool 的值。如果该转换是非法的,则程序是非法的。

[edit] 声明

如果 condition 是简单声明,则它产生的值为决策变量(参见下方)上下文转换为 bool 的值。如果该转换是非法的,则程序是非法的。

[edit] 非结构化绑定声明

该声明具有以下限制:

该声明的决策变量是声明的变量。

结构化绑定声明

该声明具有以下限制:

该声明的决策变量是声明 引入 的虚拟变量 e

(自 C++26 起)

[edit] 分支选择

如果 condition 的结果是 true,则执行 statement-true

如果 if 语句存在 else 部分,并且 condition 的结果是 false,则执行 statement-false

如果 if 语句存在 else 部分,并且 statement-true 也是 if 语句,则该内部 if 语句也必须包含 else 部分(换句话说,在嵌套的 if 语句中,else 与最接近的 if 相关联,该 if 尚未与 else 相关联)。

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

[edit] 注释

如果 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
}

如果 statement-true 通过 gotolongjmp 进入,则不会对 condition 求值,也不会执行 statement-false

条件constexpr if 语句中,不允许使用内置转换,除非是不进行 收窄整型转换bool 类型。

(自 C++17 起)
(直到 C++23)
功能测试宏 Std 特性
__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 中相同)
the condition is not evaluated and the second
substatement is not executed (same as in C)

[编辑] 另请参阅

检测调用是否发生在常量评估上下文中
(函数) [编辑]
C 文档 for if 语句