泛型选择

来自cppreference.com
< c‎ | language

提供一种基于控制表达式的类型,在编译时选择数个表达式之一的方法。

语法

_Generic ( controlling-expression , association-list ) (C11 起)

其中 association-list 是逗号分隔的关联列表,每个关联拥有语法:

type-name : expression
default : expression

其中

type-name - 任何完整的非可变更改的对象类型(即既非 VLA 亦非指向 VLA 的指针)。
controlling-expression - 任何表达式(除了逗号运算符),若不使用 default 关联,则其类型必须与 type-name 之一兼容,
expression - 任何类型和值类别的表达式(除了逗号运算符

association-list 中的任意二个 type-name 不能指定兼容类型。使用关键词 default 的关联只能有一个。若不使用 default ,且无一 type-name 与控制表达式类型兼容,则程序无法编译。

解释

首先, controlling-expression 的类型经历左值转换。只在类型域中进行转换:它舍弃顶层 cvr 限定符和原子属性,并应用数组到指针/函数到指针变换到控制表达式的类型,而不实例化任何副效应或计算任何值。

将转换后的类型与来自关联列表中的 type-name 比较。

若其类型与关联之一的 type-name 兼容,则泛型选择的类型、值及值类别就是出现于该 type-name 冒号后的表达式的类型、值及值类别。

若无一 type-name 的类型与 controlling-expression 兼容,且提供了 default 关联,则泛型的类型、值及值类别就是出现于 default : 标号后的表达式的类型、值及值类别。

注意

决不求值 controlling-expression 和未被选择的选项中的 expression

因为左值转换, "abc" 匹配 char* 而非 char[4](int const){0} 匹配 int 而非 const int 。这是由 C17 DR481 指定的(回溯应用到 C11 )。

泛型选择中的 expression 允许任何值类别,含函数指代器及 void 表达式,而且若它受到选择,则泛型选择自身拥有相同的值类别。

C99 中引入的来自 <tgmath.h>泛型数学宏,曾是通过编译器限定的行为实现的。 C11 所引入的泛型选择,给予程序员写相似的类型依赖代码的能力。

泛型选择类似 C++ 中的重载(在编译时基于参数类型选择数个函数之一)。除了它在任意表达式之间选择。

关键词

_Generic, default

示例

#include <stdio.h>
#include <math.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

引用

  • C11 standard (ISO/IEC 9899:2011):
  • 6.5.1.1 Generic selection (p: 78-79)