Skip to content
📖 Auto 版 Modern C本书目录

第十八章:类型泛型编程 ​

级别 3 -- 经验

_Generic、类型推断和匿名函数——在 C 中编写跨类型工作的代码,以及 Auto 的泛型系统如何使其自然化。

C 被设计为类型化语言:每个对象都有类型,操作针对特定类型定义。但实际程序需要不管数据的精确类型都能操作。C 已经演化出类型泛型编程的机制——_Generic、auto、typeof——Auto 通过完整的泛型系统进一步推进了这些想法。


18.1 固有的类型泛型特性 ​

C 一直通过转换和提升具有隐式类型泛型特性:

c
// C 深入:隐式转换
short s = 42;
int i = s;          // short 提升为 int(整数提升)
long l = i;         // int 转换为 long(通常转换)
double d = l;       // long 转换为 double(通常转换)

int a = 3;
double b = 4.2;
double c = a + b;   // a 在加法前被转换为 double

通常算术转换规则确定二元操作的公共类型:

  1. 对两个操作数执行整数提升。
  2. 如果类型不同,"较窄"的类型被转换为"较宽"的类型。
  3. 如果两者都是整数类型,有符号性根据标准中的复杂规则解决。

C 深入: C 中的整数提升规则经常令人惊讶。在大多数平台上,short 值在任何算术操作之前被提升为 int。这意味着 short a, b; auto c = a + b; 产生 int,而不是 short。这些规则还以不明显的方式与有符号性交互:unsigned int 对 long 取决于平台大小。

这些转换是自动的但不总是安全的:

c
// C 深入:转换陷阱
int big = 2147483647;    // 32 位 int 的 INT_MAX
float f = big;            // 可能丢失精度(float 有 23 位尾数)
int back = f;             // 如果 f > INT_MAX,实现定义的行为

unsigned int u = -1;     // 定义良好的:通过模转换得到 UINT_MAX
int signed_val = u;       // 如果 u > INT_MAX,实现定义的行为

Auto 的方法。 Auto 要求类型之间的显式转换。没有隐式窄化转换:

auto
// Auto:显式转换
fn main() {
    let i int = 42
    let d float = float(i)     // 显式转换
    let s int = int(d)         // 显式转换
    print("i:", i)
    print("d:", d)
    print("s:", s)
}

18.2 泛型选择 ​

C11 引入了 _Generic 用于编译时类型分发:

c
// C 深入:_Generic
#include <stdio.h>

#define print_val(x) _Generic((x), \
    int: print_int,                \
    double: print_double,          \
    char*: print_string            \
)(x)

void print_int(int x) { printf("%d\n", x); }
void print_double(double x) { printf("%f\n", x); }
void print_string(char *x) { printf("%s\n", x); }

int main(void) {
    print_val(42);            // 调用 print_int
    print_val(3.14);          // 调用 print_double
    print_val("hello");       // 调用 print_string
    return 0;
}

_Generic 是编译时构造。编译器根据控制表达式的类型选择匹配的关联。它不在运行时求值表达式。

C 深入: _Generic 功能强大但冗长。每种类型需要自己的函数,映射必须手动维护。它对限定类型也有令人惊讶的行为:const int 和 int 对 _Generic 是不同类型,所以你可能需要两者的条目。

&lt;tgmath.h&gt; 头文件使用 _Generic 提供类型泛型数学函数:

c
// C 深入:tgmath.h
#include <tgmath.h>

double d = sqrt(2.0);       // 调用 sqrt(double)
float f = sqrtf(2.0f);      // 调用 sqrtf(float) -- 但有 tgmath:
float g = sqrt(2.0f);       // 也调用 sqrtf!类型泛型分发

Auto 的方法。 Auto 计划通过 spec 约束提供泛型:

auto
// Auto:带 spec 约束的泛型(计划中)
// fn add<T>(a T, b T) T where T: Numeric {
//     a + b
// }

// 目前,使用单独的函数或 comptime 分发
fn generic_add(a int, b int) int {
    a + b
}

fn generic_add_f(a float, b float) float {
    a + b
}

基于 MIT 许可发布。