静态派发

类型擦除

类型擦除

类型擦除(Type erasure)是一种技术:把多种具体类型包装成统一的接口,从而在使用端忽略底层类型差异。它实现了“不依赖虚函数”的多态。

典型例子:std::function

#include <functional>
#include <iostream>

void lambda_call() {
    std::function<int(int, int)> op;

    op = [](int a, int b) { return a + b; };
    std::cout << op(1, 2); // 3

    op = [](int a, int b) { return a * b; };
    std::cout << op(1, 2); // 2
}

std::function 内部可以存储普通函数、Lambda、函数对象、成员函数等,但对外暴露统一接口。它通常通过虚函数或函数指针实现,是运行时类型擦除。

手动实现一个简单的类型擦除

class Animal {
    struct Concept {
        virtual ~Concept() = default;
        virtual void speak() const = 0;
    };

    template <typename T>
    struct Model : Concept {
        T data;
        Model(T d) : data(std::move(d)) {}
        void speak() const override { data.speak(); }
    };

    std::unique_ptr<Concept> p;

public:
    template <typename T>
    Animal(T x) : p(std::make_unique<Model<T>>(std::move(x))) {}

    void speak() const { p->speak(); }
};

struct Cat { void speak() const { std::cout << "\n"; } };
struct Dog { void speak() const { std::cout << "\n"; } };

Animal a = Cat{};
Animal b = Dog{};
a.speak(); // 喵
b.speak(); // 汪

这里用了一个内部虚基类 Concept 和模板派生类 Model。外部接口 Animal 不暴露具体类型,因此实现了类型擦除。

与模板静态多态的区别

特性类型擦除模板静态多态
类型信息运行时隐藏编译期确定
容器内混合类型可以(如 std::vector<Animal>不行(除非手动擦除)
开销通常有堆分配和虚调用零开销

类型擦除牺牲了部分性能,换取了运行时的灵活性。标准库中的 std::anystd::function、多态分配器都是典型应用。