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

如何编写可从lambda构造的仿函数并提取其中的this指针?

自定义仿函数实现小对象优化并存储捕获的this指针

核心思路

std::function的小对象优化依赖内部固定大小缓冲区存储小型可调用对象(比如仅捕获this的lambda,大小通常等于指针长度),以此避免堆分配。自定义仿函数要实现类似逻辑,核心是:

  • 用栈上固定大小缓冲区存储捕获this的lambda(或直接存储this指针+调用逻辑)
  • 通过类型擦除实现统一调用接口,同时支持提取存储的this指针

具体实现步骤

1. 设计仿函数内存布局

自定义仿函数需要包含两部分核心成员:

  • 对齐的固定大小缓冲区:尺寸参考std::function的小对象缓冲区(通常24字节),用于容纳小型可调用对象
  • 函数指针:分别负责调用可调用对象和销毁它,实现类型擦除

2. 从捕获this的lambda构造

仅捕获this的lambda本质是编译器生成的匿名类,大小等于指针长度。我们可以直接将lambda对象拷贝到缓冲区中,无需堆分配。

3. 实现调用与this提取逻辑

通过存储的函数指针触发lambda调用;若要提取this,可直接访问lambda的内部成员(依赖编译器实现),或在构造时显式提取this单独存储。

代码示例

版本1:存储完整lambda(依赖编译器实现)

#include <cstddef>
#include <type_traits>
#include <utility>
#include <cstdio>

// 自定义仿函数,支持小对象优化
class MyFunction {
public:
    MyFunction() noexcept : invoke_(nullptr), destroy_(nullptr) {}

    // 从捕获this的lambda构造
    template<typename F>
    MyFunction(F&& f) noexcept(std::is_nothrow_move_constructible_v<F>) {
        static_assert(sizeof(F) <= sizeof(buffer_), "Callable exceeds small object buffer size");
        static_assert(alignof(F) <= alignof(decltype(buffer_)), "Callable alignment requirement not met");

        // 将lambda拷贝到缓冲区
        new (buffer_) F(std::forward<F>(f));
        // 设置调用和销毁逻辑
        invoke_ = [](void* buf) { (*static_cast<F*>(buf))(); };
        destroy_ = [](void* buf) { static_cast<F*>(buf)->~F(); };
    }

    ~MyFunction() noexcept {
        if (destroy_) destroy_(buffer_);
    }

    void operator()() const {
        if (invoke_) invoke_(buffer_);
    }

    // 提取存储的this指针(依赖GCC/Clang的lambda内部成员命名)
    template<typename T>
    T* get_this() const noexcept {
        auto* lambda = static_cast<std::decay_t<decltype(*this)>::LambdaType*>(buffer_);
        return lambda->__this;
    }

private:
    alignas(std::max_align_t) char buffer_[24];
    void (*invoke_)(void*);
    void (*destroy_)(void*);

    // 辅助类型推导
    template<typename T>
    struct LambdaTypeHelper;
    template<typename C, typename R>
    struct LambdaTypeHelper<R(C::*)()> { using type = C; };
};

// 测试
class TestClass {
public:
    void func() {
        auto lambda = [this]() {
            printf("Lambda called, this = %p\n", this);
        };

        MyFunction func_obj(lambda);
        func_obj();

        TestClass* extracted_this = func_obj.get_this<TestClass>();
        printf("Extracted this = %p\n", extracted_this);
    }
};

int main() {
    TestClass tc;
    tc.func();
    return 0;
}

版本2:直接存储this与调用逻辑(可移植性更高)

如果不想依赖编译器的lambda内部实现,可以直接存储this指针和成员函数指针:

#include <cstdio>

class SimplifiedMyFunction {
public:
    template<typename C>
    SimplifiedMyFunction(C* obj, void (C::*func)()) noexcept 
        : obj_(obj), func_([](void* obj) {
            auto* typed_obj = static_cast<C*>(obj);
            (typed_obj->*func)();
        }) {}

    void operator()() const {
        func_(obj_);
    }

    template<typename C>
    C* get_this() const noexcept {
        return static_cast<C*>(obj_);
    }

private:
    void* obj_;
    void (*func_)(void*);
};

// 测试
class TestClass {
public:
    void func() {
        SimplifiedMyFunction func_obj(this, &TestClass::impl);
        func_obj();
        printf("Extracted this = %p\n", func_obj.get_this<TestClass>());
    }

private:
    void impl() {
        printf("Impl called, this = %p\n", this);
    }
};

int main() {
    TestClass tc;
    tc.func();
    return 0;
}

关键注意事项

  • 缓冲区对齐与大小:用alignas(std::max_align_t)保证缓冲区兼容所有基础类型的对齐要求,大小可根据目标平台调整(比如64位系统下24字节足够容纳多数小型可调用对象)
  • 可移植性:版本1依赖编译器对lambda的内部命名规则,版本2通过显式存储this和成员函数指针,兼容性更强
  • 资源安全:务必在析构函数中销毁缓冲区中的lambda对象,避免内存泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 18:15:26