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

如何为仿std::function的函数容器类添加整型模板参数实现小缓冲区优化?

为自定义函数容器添加可配置小缓冲区优化的实现方案

你需要给MyDynamicCallable添加可配置的小缓冲区大小参数,核心思路是在类中嵌入固定大小的栈缓冲区,当可调用对象的大小和对齐要求能被缓冲区满足时,直接在栈上存储对象以避免堆分配;否则沿用原有的堆内存分配逻辑。以下是具体实现步骤和代码:

核心修改要点

  • 给类模板增加非类型模板参数BufferSize,用于指定小缓冲区大小,可设置默认值(比如16,和部分std::function实现一致)
  • 在类内部添加对齐的小缓冲区存储,同时增加标记位区分当前使用栈缓冲区还是堆内存
  • 修改构造逻辑:判断可调用对象的大小和对齐要求是否适配缓冲区,选择对应存储方式
  • 完善reset函数,确保正确调用对象析构并释放堆内存(如果使用了的话)

修改后的完整代码

#include <type_traits>
#include <utility>

// 假设MemMngr是已实现的内存管理类
struct MemMngr {
    static void* mem_alloc(size_t size) {
        return operator new(size);
    }
    static void mem_free(void* ptr) {
        operator delete(ptr);
    }
};

template <typename T, size_t BufferSize = 16>
class MyDynamicCallable;

template <typename ret_t, typename... arg_types, size_t BufferSize>
class MyDynamicCallable<ret_t(arg_types...), BufferSize> {
public:
    template <typename callable_t>
    MyDynamicCallable(callable_t&& callable) {
        using CallableType = std::decay_t<callable_t>;
        constexpr size_t obj_size = sizeof(CallableType);
        constexpr size_t obj_align = alignof(CallableType);

        // 检查对象是否能放入小缓冲区且对齐要求满足
        if (obj_size <= BufferSize && obj_align <= alignof(decltype(buffer))) {
            // 使用栈上缓冲区
            is_heap_allocated = false;
            new (get_buffer_ptr()) CallableType(std::forward<callable_t>(callable));
        } else {
            // 走堆分配
            is_heap_allocated = true;
            obj_ptr = MemMngr::mem_alloc(obj_size);
            new (obj_ptr) CallableType(std::forward<callable_t>(callable));
        }
        // 设置调用函数指针
        call_func_ptr = &call_object<CallableType>;
        // 保存析构函数指针,用于reset时清理
        destroy_func_ptr = &destroy_object<CallableType>;
    }

    // 移动构造
    MyDynamicCallable(MyDynamicCallable&& other) noexcept
        : call_func_ptr(other.call_func_ptr),
          destroy_func_ptr(other.destroy_func_ptr),
          is_heap_allocated(other.is_heap_allocated) {
        if (other.is_heap_allocated) {
            obj_ptr = other.obj_ptr;
            other.obj_ptr = nullptr;
        } else {
            // 复制栈缓冲区内容(需对象支持移动或复制)
            std::memcpy(buffer, other.buffer, BufferSize);
        }
        other.call_func_ptr = nullptr;
        other.destroy_func_ptr = nullptr;
    }

    // 移动赋值运算符
    MyDynamicCallable& operator=(MyDynamicCallable&& other) noexcept {
        if (this != &other) {
            reset();
            call_func_ptr = other.call_func_ptr;
            destroy_func_ptr = other.destroy_func_ptr;
            is_heap_allocated = other.is_heap_allocated;
            if (other.is_heap_allocated) {
                obj_ptr = other.obj_ptr;
                other.obj_ptr = nullptr;
            } else {
                std::memcpy(buffer, other.buffer, BufferSize);
            }
            other.call_func_ptr = nullptr;
            other.destroy_func_ptr = nullptr;
        }
        return *this;
    }

    // 禁止拷贝构造和拷贝赋值
    MyDynamicCallable(const MyDynamicCallable&) = delete;
    MyDynamicCallable& operator=(const MyDynamicCallable&) = delete;

    ~MyDynamicCallable() {
        reset();
    }

    void reset() {
        if (destroy_func_ptr) {
            destroy_func_ptr(get_object_ptr());
            destroy_func_ptr = nullptr;
            call_func_ptr = nullptr;
        }
        if (is_heap_allocated && obj_ptr) {
            MemMngr::mem_free(obj_ptr);
            obj_ptr = nullptr;
            is_heap_allocated = false;
        }
    }

    ret_t operator()(arg_types... args) const {
        return call_func_ptr(get_object_ptr(), std::forward<arg_types>(args)...);
    }

private:
    // 小缓冲区,用aligned_storage确保对齐
    typename std::aligned_storage<BufferSize, alignof(std::max_align_t)>::type buffer;
    void* obj_ptr = nullptr;
    ret_t (*call_func_ptr)(void*, arg_types...) = nullptr;
    void (*destroy_func_ptr)(void*) = nullptr;
    bool is_heap_allocated = false;

    // 获取对象指针
    void* get_object_ptr() const {
        return is_heap_allocated ? obj_ptr : const_cast<void*>(static_cast<const void*>(&buffer));
    }

    template <typename callable_t>
    static ret_t call_object(void* obj, arg_types... args) {
        return (*static_cast<callable_t*>(obj))(std::forward<arg_types>(args)...);
    }

    template <typename callable_t>
    static void destroy_object(void* obj) {
        static_cast<callable_t*>(obj)->~callable_t();
    }
};

关键细节说明

  1. 缓冲区对齐:使用std::aligned_storage并指定std::max_align_t作为对齐值,确保缓冲区能满足绝大多数类型的对齐要求;若需更精准对齐,可根据可调用对象的alignof动态判断,但静态缓冲区仅能满足不超过自身对齐要求的对象。
  2. 析构处理:新增destroy_func_ptr存储对应类型的析构函数指针,确保reset或析构时能正确销毁对象,避免内存泄漏。
  3. 移动语义:补充移动构造和移动赋值运算符,处理栈缓冲区内容复制与堆指针转移,符合容器的移动语义要求。
  4. 模板参数默认值:给BufferSize设置默认值16,用户既可以用MyDynamicCallable<void()>使用默认缓冲区,也可以通过MyDynamicCallable<void(), 32>自定义大小。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 17:53:29