Namespaces
Variants

Generic selection (since C11)

From cppreference.net

提供了一种在编译时根据控制表达式类型从多个表达式中选择其一的机制

目录

语法

_Generic ( 控制表达式 , 关联列表 ) (自 C11 起)

其中 association-list 是一个以逗号分隔的关联列表,每个关联具有以下语法

类型名 : 表达式
default : 表达式

其中

type-name - 任何完整的 对象类型 ,且不能是可变修改类型(即不能是VLA或指向VLA的指针)。
controlling-expression - 任意表达式(除 逗号运算符 外),若未使用 default 关联,其类型必须与某个 type-name 兼容
expression - 任意类型和值类别的表达式(除 逗号运算符 外)

association-list 中,任意两个 type-name 不得指定 兼容类型 。最多只能存在一个使用关键字 default 的关联项。若未使用 default 且所有 type-name 均与控制表达式类型不兼容,则程序将无法编译。

说明

首先, 控制表达式 的类型会经历 左值转换 。该转换仅在类型域中进行:它会丢弃顶层的 cvr 限定符和原子性,并对控制表达式的类型应用数组到指针/函数到指针的转换,而不会引发任何副作用或计算任何值。

转换后的类型将与关联列表中的 类型名称 进行比较。

如果类型与某个关联的 类型名 兼容 ,则泛型选择的类型、值和 值类别 即为该 类型名 对应冒号后出现的 表达式 的类型、值和值类别。

如果没有任何 类型名 控制表达式 的类型兼容,且提供了 default 关联项,则泛型选择的类型、值和值类别将是 default : 标签后表达式的类型、值和值类别。

注释

未被选中的选择分支中的 控制表达式 表达式 永远不会被求值。

由于左值转换, "abc" 匹配 char * 而非 char [ 4 ] ,且 ( int const ) [ 0 ] 匹配 int ,而非 const int

所有 值类别 ,包括函数指示符和void表达式,都可以作为泛型选择中的 表达式 使用,如果被选中,泛型选择本身具有相同的值类别。

C99 引入的 <tgmath.h> 头文件中的 泛型数学宏 是通过编译器特定方式实现的。C11 引入的泛型选择机制使程序员能够编写类似的类型相关代码。

泛型选择类似于 C++ 中的重载(根据参数类型在编译时从多个函数中选择其一),不同之处在于它是在任意表达式之间进行选择。

关键词

_Generic default

示例

#include <math.h>
#include <stdio.h>
// tgmath.h 宏 cbrt 的可能实现
#define cbrt(X) _Generic((X),     \
              long double: cbrtl, \
                  default: cbrt,  \
                    float: cbrtf  \
              )(X)
int main(void)
{
    double x = 8.0;
    const float y = 3.375;
    printf("cbrt(8.0) = %f\n", cbrt(x));    // 选择默认的 cbrt
    printf("cbrtf(3.375) = %f\n", cbrt(y)); // 将 const float 转换为 float,
                                            // 然后选择 cbrtf
}

输出:

cbrt(8.0) = 2.000000
cbrtf(3.375) = 1.500000

缺陷报告

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

DR 适用范围 已发布行为 正确行为
DR 481 C11 未明确说明控制表达式是否进行左值转换 需进行左值转换

参考文献

  • C23 标准 (ISO/IEC 9899:2024):
  • 6.5.1.1 泛型选择 (p: TBD)
  • C17 标准 (ISO/IEC 9899:2018):
  • 6.5.1.1 泛型选择 (页码: 56-57)
  • C11 标准 (ISO/IEC 9899:2011):
  • 6.5.1.1 泛型选择 (p: 78-79)

参阅

C++ 文档 关于 Templates