Namespaces
Variants

switch statement

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
switch
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

根据条件表达式的值,将控制权转移至多个语句中的某一个。

目录

语法

attr  (可选) switch ( init-statement  (可选) condition ) statement
attr - (C++11 起) 任意数量的 属性
init-statement - (C++17 起) 以下任意一项:
(C++23 起)

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

condition - 一个 条件
statement - 一条语句(通常是复合语句)

条件表达式

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

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

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

表达式

如果 condition 是一个表达式,其产生的值即为该表达式的值。

声明

如果 condition 是一个简单声明,其产生的值即为决策变量的值(详见下文)。

非结构化绑定声明

该声明具有以下限制:

  • 语法上符合以下形式:
  • 类型说明符序列 声明符 = 赋值表达式
(C++11 前)
  • 属性说明符序列 (可选) 声明说明符序列 声明符 花括号或等号初始化器
(C++11 起)

声明的决策变量即为所声明的变量。

结构化绑定声明

该声明具有以下限制:

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

(自 C++26 起)

类型

condition 仅可生成以下类型:

  • 整型
  • 枚举类型
  • 类类型

如果产生的值是类类型,则会在上下文中隐式转换为整数或枚举类型。

如果(可能转换后的)类型适用于 整型提升 ,则产生的值将转换为提升后的类型。

标签

switch 语句内的任何语句都可以使用以下一种或多种标签进行标注:

attr  (可选) case constant-expression : (1)
attr  (可选) default: (2)
attr - (since C++11) 任意数量的 属性
constant-expression - 符合 switch 条件调整后类型的 转换后常量表达式


一个 case default 标签与其最内层包围它的 switch 语句相关联。

若满足以下任一条件,则程序非良构:

  • 一个 switch 语句关联了多个 case 标签,这些标签的 常量表达式 在转换后具有相同的值。
  • 一个 switch 语句关联了多个 default 标签。

控制流转移

switch 语句的条件产生一个(可能经过转换的)值时:

  • 如果某个关联的 case 标签常量具有相同值,控制权将传递给由匹配的 case 标签标记的语句。
  • 否则,如果存在关联的 default 标签,控制权将传递给由 default 标签标记的语句。
  • 否则, switch 语句中的所有语句都不会被执行。

case default 标签本身不会改变控制流。要从 switch 语句中间退出,请参阅 break 语句

编译器可能会对直落(在没有 break 的情况下进入下一个 case default 标签)发出警告 ,除非紧邻 case 标签前出现 [[ fallthrough ]] 属性以表明该直落行为是预期的 (C++17 起)

switch (1)
{
    case 1:
        std::cout << '1'; // 输出"1",
    case 2:
        std::cout << '2'; // 随后输出"2"
}
switch (1)
{
    case 1:
        std::cout << '1'; // 输出 "1"
        break;            // 并退出 switch 语句
    case 2:
        std::cout << '2';
        break;
}

带初始化器的 switch 语句

若使用 初始化语句 ,则 switch 语句等价于

{
初始化语句
switch ( 条件 ) 语句

}

不同之处在于:由 初始化语句 (若 初始化语句 是声明)声明的名称,以及由 条件 (若 条件 是声明)声明的名称处于同一作用域,该作用域也是 语句 的作用域。

(C++17 起)

注释

由于控制流 不允许跳转至变量作用域内 ,如果在 语句 内部遇到声明语句,该声明必须被限定在自身的复合语句作用域中:

switch (1)
{
    case 1:
        int x = 0; // 初始化
        std::cout << x << '\n';
        break;
    default:
        // 编译错误:跳转到 default 标签
        // 将进入 'x' 的作用域但未初始化该变量
        std::cout << "default\n";
        break;
}
switch (1)
{
    case 1:
        {
            int x = 0;
            std::cout << x << '\n';
            break;
        } // 'x' 的作用域在此结束
    default:
        std::cout << "default\n"; // 无错误
        break;
}

关键词

switch case default

示例

以下代码展示了 switch 语句的几种使用场景:

#include <iostream>
int main()
{
    const int i = 2;
    switch (i)
    {
        case 1:
            std::cout << '1';
        case 2:              // 执行从此 case 标签开始
            std::cout << '2';
        case 3:
            std::cout << '3';
            [[fallthrough]]; // C++17 属性,用于消除贯穿警告
        case 5:
            std::cout << "45";
            break;           // 终止后续语句的执行
        case 6:
            std::cout << '6';
    }
    std::cout << '\n';
    switch (i)
    {
        case 4:
            std::cout << 'a';
        default:
            std::cout << 'd'; // 没有匹配的常量表达式
                              // 因此执行 default 分支
    }
    std::cout << '\n';
    switch (i)
    {
        case 4:
            std::cout << 'a'; // 未执行任何语句
    }
    // 当在 switch 语句中使用枚举时,如果某个枚举项未被处理
    // 许多编译器会发出警告
    enum color { RED, GREEN, BLUE };
    switch (RED)
    {
        case RED:
            std::cout << "red\n";
            break;
        case GREEN:
            std::cout << "green\n";
            break;
        case BLUE:
            std::cout << "blue\n";
            break;
    }
    // 当不存在隐式转换到整型或枚举类型时
    // C++17 的初始化语句语法会很有帮助
    struct Device
    {
        enum State { SLEEP, READY, BAD };
        auto state() const { return m_state; }
        /* ... */
    private:
        State m_state{};
    };
    switch (auto dev = Device{}; dev.state())
    {
        case Device::SLEEP:
            /* ... */
            break;
        case Device::READY:
            /* ... */
            break;
        case Device::BAD:
            /* ... */
            break;
    }
    // 特殊示例
    // 语句不必是复合语句
    switch (0)
        std::cout << "this does nothing\n";
    // 标签也不需要复合语句
    switch (int n = 1)
        case 0:
        case 1:
            std::cout << n << '\n';
}

输出:

2345
d
red
1

缺陷报告

下列行为变更缺陷报告被追溯应用于先前发布的C++标准。

缺陷报告 适用标准 发布时行为 正确行为
CWG 1767 C++98 不适用于整型提升的类型所对应的 condition 无法被提升 不对这些类型的
condition 进行提升
CWG 2629 C++98 condition 可以是浮点变量的声明 已禁止

参见

C 文档 关于 switch

外部链接

1. 使用达夫设备实现循环展开
2. 达夫设备可用于在C/C++中实现协程