You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

请教类型擦除及std::function中"switch and op code"技术细节

"Switch and Op Code":std::function的一种底层实现模式

定义

这里提到的"switch and op code"是一种用操作码枚举配合switch分支实现类型擦除的模式,曾是std::function的经典实现思路之一:

  • 给每个需要支持的操作(比如调用、销毁、拷贝)分配唯一的"操作码"(用枚举表示)
  • 将具体类型的操作逻辑封装成统一签名的函数,把函数指针和对应操作码绑定
  • 运行时通过操作码,用switch语句分发到对应类型的具体实现,从而隐藏底层真实类型——这就是类型擦除的核心:对外暴露统一接口,内部根据操作码适配不同类型

设计动机

这种模式被用于std::function的核心原因:

  • 兼容早期C++环境:在C++11之前,模板元编程和虚函数优化尚不完善,这种方式用简单语法就能实现类型擦除,无需复杂模板技巧
  • 灵活可控:可以精准定义支持的操作集合,比如只保留调用逻辑、省略拷贝,避免不必要的性能开销
  • 性能优势:相比虚函数的间接调用,switch分支可被编译器优化为跳转表,减少虚表查找的开销,尤其在操作类型较少的场景下更明显

使用示例(简化版std::function)

下面是一个用该模式实现的轻量级可调用对象包装器,模拟std::function<void()>的核心逻辑:

#include <cstdint>
#include <memory>
#include <iostream>

// 定义操作码:标记需要执行的操作类型
enum class OpCode : uint8_t {
    Invoke,   // 调用可调用对象
    Destroy,  // 销毁底层对象
};

// 统一的函数指针签名,适配所有操作
using FuncPtr = void(*)(void*);

// 类型擦除的核心存储结构
struct ErasedWrapper {
    void* data;               // 指向真实可调用对象的指针
    FuncPtr handlers[2];      // 对应操作码的处理函数指针
};

// 模板生成具体类型的操作逻辑
template<typename Callable>
void invoke_handler(void* data) {
    (*static_cast<Callable*>(data))();
}

template<typename Callable>
void destroy_handler(void* data) {
    delete static_cast<Callable*>(data);
}

// 包装器类
class SimpleFunction {
private:
    ErasedWrapper wrapper_;

public:
    // 构造函数:接受任意可调用对象
    template<typename Callable>
    SimpleFunction(Callable&& func) {
        wrapper_.data = new Callable(std::forward<Callable>(func));
        // 绑定操作码对应的处理函数
        wrapper_.handlers[static_cast<int>(OpCode::Invoke)] = &invoke_handler<Callable>;
        wrapper_.handlers[static_cast<int>(OpCode::Destroy)] = &destroy_handler<Callable>;
    }

    // 重载调用运算符:通过switch分发操作
    void operator()() {
        switch (OpCode::Invoke) {
            case OpCode::Invoke:
                wrapper_.handlers[static_cast<int>(OpCode::Invoke)](wrapper_.data);
                break;
            default:
                break;
        }
    }

    // 析构函数:销毁底层对象
    ~SimpleFunction() {
        wrapper_.handlers[static_cast<int>(OpCode::Destroy)](wrapper_.data);
    }
};

// 测试代码
int main() {
    // 包装lambda表达式
    SimpleFunction f1([](){ std::cout << "Hello from erased lambda!\n"; });
    f1();

    // 包装普通函数
    void regular_func() { std::cout << "Hello from regular function!\n"; }
    SimpleFunction f2(regular_func);
    f2();

    return 0;
}

与std::function的关联

标准库中的std::function现在多采用小型对象优化+虚函数表的实现,但早期版本确实依赖这种"操作码+switch分发"的思路:把std::function支持的所有操作(调用、拷贝、移动、销毁等)都用操作码标记,通过switch或函数指针数组分发到具体类型的实现,从而完成对任意可调用对象的类型擦除包装。

内容的提问来源于stack exchange,提问作者Enlico

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 16:03:26