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

如何创建带有编译期静态删除器的std::unique_ptr(无需运行时函数指针)

如何在类中定义带编译期静态删除器的std::unique_ptr?

你的需求完全可以实现!核心要点是:让删除器的类型是一个无状态的可调用类型(比如无捕获Lambda、空函数对象类),而不是函数指针类型——后者会迫使std::unique_ptr额外存储删除器指针(导致大小翻倍),而无状态的可调用类型可以被编译器优化,不需要存储任何额外数据,完美契合你"编译期确定删除器、分配时无需传函数指针"的需求。

先拆解你最初的测试代码困惑

  1. Lambda删除器的成功案例
    你第一个测试里的无捕获Lambda,它的类型是一个编译器生成的空类(无状态)。当你用decltype(deleter)作为unique_ptr的删除器模板参数时,unique_ptr不需要存储这个删除器的实例——因为删除器的行为是编译期完全确定的,编译器可以直接调用Lambda的operator(),不需要额外存储任何东西,所以ptr1的大小是8字节(和原始指针一致),而且构造时也不用传删除器参数。

  2. 函数指针删除器的问题
    第二个测试里你用了decltype(&staticDeleter),这本质是函数指针类型。函数指针是运行时的值(哪怕你用的是静态函数,类型依然是指针),所以unique_ptr必须存储这个指针才能在析构时调用,这就是为什么ptr2大小是16字节,而且构造时必须显式传递staticDeleter——因为函数指针类型没有默认构造函数,编译器不知道你要指向哪个函数。

你修正后的代码为什么有效?

你后来在Container里用了一个无捕获Lambda作为删除器类型:

using deleter = decltype([](Apa* apa) { std::cout << "deleter\n"; delete apa; });

这个Lambda是无捕获的,它的类型是一个空的类类型,完全符合unique_ptr的优化条件。所以std::unique_ptr<Apa, deleter>不需要存储删除器实例,大小保持8字节,而且构造时可以直接用new Apa初始化,不需要额外传递删除器——因为编译器可以默认构造这个无状态的删除器类型。

另一种实现方式:自定义空删除器类

如果你不想用Lambda,也可以手动定义一个无状态的函数对象类,效果完全一样:

#include <memory>
#include <iostream>

struct Apa{
    ~Apa() { std::cout << "Apa deleted\n"; }
};

// 自定义静态删除器类,无状态
struct StaticDeleter {
    void operator()(Apa* a) const {
        std::cout << "static deleter\n";
        delete a;
    }
};

struct Container {
    using deleter = StaticDeleter;
    // 构造时无需传递删除器,编译器会默认构造StaticDeleter
    std::unique_ptr<Apa, deleter> ptr{new Apa};
};

int main() {
    std::cout << sizeof(Container::ptr) << "\n";
    Container container{};
}

这段代码的输出和你修正后的Lambda版本完全一致,unique_ptr的大小也是8字节。

总结

只要满足以下两个条件,就能实现你想要的效果:

  • 删除器类型是无状态的(没有非静态成员变量,比如无捕获Lambda、空函数对象类)
  • 删除器类型是可默认构造的

此时std::unique_ptr会在编译期确定删除器的行为,不需要存储任何删除器实例,分配内存时也无需传递删除器指针,完全符合你的需求。

内容的提问来源于stack exchange,提问作者Lasersköld

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 06:47:33