如何将类静态方法作为std::unique_ptr的删除器并解决相关编译及类型别名问题
问题原因
你遇到的编译错误来自旧版本GCC对应的libstdc++实现限制:当std::unique_ptr的删除器类型是函数指针时,用nullptr直接构造unique_ptr会默认将删除器也初始化为空指针,触发库内的静态断言,禁止使用空函数指针作为删除器。
你代码里的静态成员初始化s_instance_{nullptr}正好触发了这个场景,哪怕后续赋值时会传入正确的删除器地址,构造阶段的断言也会先报错。
修复方案
方案1:初始化时传入删除器(最小修改)
只需要修改静态成员s_instance_的初始化语句,构造时同时传入nullptr和删除器地址即可:
// 把原来的这行 // std::unique_ptr<Singleton, decltype(&Singleton::deleter)> Singleton::s_instance_{nullptr}; // 替换为 std::unique_ptr<Singleton, decltype(&Singleton::deleter)> Singleton::s_instance_{nullptr, &Singleton::deleter};
这样构造阶段就会给删除器赋值为合法的函数地址,不会触发空指针断言。
方案2:使用空类型删除器(更优)
函数指针作为删除器会让每个unique_ptr多占用一个指针的存储空间,你可以把删除逻辑封装成一个空的仿函数类型,既不会有额外开销,也天然避免空删除器的问题:
class Singleton { public: // 定义空类型删除器 struct Deleter { void operator()(Singleton* p) const { delete p; std::cout << "In deleter\n" << std::endl; } }; // 类内直接定义别名,外部可以直接用Singleton::UPtr using UPtr = std::unique_ptr<Singleton, Deleter>; static UPtr& getInstance(void); void hello (void) { std::cout << "Hello!\n" << std::endl; } Singleton(const Singleton&) = delete; Singleton(Singleton&&) = delete; Singleton& operator=(const Singleton&) = delete; Singleton& operator=(Singleton&&) = delete; private: static UPtr s_instance_; Singleton() { std::cout << "Constructed\n" << std::endl; } ~Singleton() { std::cout << "Destroyed\n" << std::endl; } }; // 静态成员初始化直接用默认构造即可,不需要传删除器 Singleton::UPtr Singleton::s_instance_; // getInstance里构造unique_ptr也不需要传删除器 Singleton::UPtr& Singleton::getInstance (void) { if (s_instance_ == nullptr) { s_instance_ = UPtr(new Singleton()); } return s_instance_; }
别名缩短方案
你之前在类外定义别名报错,是因为此时Singleton是不完整类型,且deleter成员还未声明,编译器无法识别。
用上述方案2在类的public区域定义UPtr别名是最简洁的方式,外部所有用到该unique_ptr类型的地方都可以直接写Singleton::UPtr,不需要重复写完整的类型声明。
额外注意
你当前代码中getInstance返回unique_ptr的非const引用,外部调用reset会直接释放全局单例实例,如果这不是预期行为,建议改为返回实例的普通引用Singleton&,或者返回const引用避免外部修改内部存储的指针。
内容的提问来源于stack exchange,提问作者void
相关产品推荐
相关产品推荐

