命名空间
变体
操作

泛型选择 (自 C11 起)

来自 cppreference.cn
< c‎ | 语言

提供了一种在编译时根据控制表达式的类型选择多个表达式之一的方式

目录

[编辑] 语法

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

其中 关联列表 是以逗号分隔的关联列表,每个关联的语法如下:

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

其中

类型名称 - 任何完整的对象类型,且不是可变修改的(即不是 VLA 或指向 VLA 的指针)。
控制表达式 - 任何表达式(除了逗号运算符),如果未使用 default 关联,则其类型必须与类型名称之一兼容。
表达式 - 任何类型和值类别的表达式(除了逗号运算符

关联列表中不能有两个类型名称指定兼容类型。只能有一个使用关键字 default 的关联。如果未使用 default 且没有任何类型名称与控制表达式的类型兼容,则程序将无法编译。

[编辑] 解释

首先,控制表达式的类型会进行左值转换。转换仅在类型域中执行:它丢弃顶层 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>
 
// Possible implementation of the tgmath.h macro 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));    // selects the default cbrt
    printf("cbrtf(3.375) = %f\n", cbrt(y)); // converts const float to float,
                                            // then selects cbrtf
}

输出

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

[编辑] 缺陷报告

以下行为改变的缺陷报告被追溯地应用于以前发布的 C 标准。

缺陷报告 应用于 发布时的行为 正确的行为
DR 481 C11 未明确控制表达式是否进行左值转换 它进行了

[编辑] 参考

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

[编辑] 另请参阅

C++ 文档,关于 模板