如何编写适配std::shared_ptr与std::unique_ptr的模板化工厂函数
复用智能指针工厂函数的模板化实现
问题场景
我经常编写一对功能高度重复的工厂函数,分别返回std::unique_ptr和std::shared_ptr:
std::unique_ptr<AbstractType> createUnique(IDType id); std::shared_ptr<AbstractType> createShared(IDType id);
两者的核心逻辑完全一致,仅在创建实例的函数(std::make_unique/std::make_shared)和返回的智能指针类型上有区别:createUnique实现示例:
switch (id) { case kID1: return std::make_unique<Type1>(); // 其他类型分支... }
createShared实现示例:
switch (id) { case kID1: return std::make_shared<Type1>(); // 其他类型分支... }
这种重复不仅增加维护成本,也不符合DRY原则。我希望实现一个通用模板函数,能同时适配两种智能指针类型,避免代码重复,同时保留直接调用对应创建函数的效率(不通过智能指针间的转换实现)。
解决方案
方案1:类型特征绑定指针与创建逻辑
通过定义类型特征模板,将智能指针类型与其对应的创建函数绑定,用户只需指定指针类型即可调用通用工厂:
首先定义类型特征:
#include <memory> template <typename PTR> struct creator_trait; // 为std::unique_ptr特化特征 template <typename T> struct creator_trait<std::unique_ptr<T>> { template <typename Derived> static std::unique_ptr<T> make() { return std::make_unique<Derived>(); } }; // 为std::shared_ptr特化特征 template <typename T> struct creator_trait<std::shared_ptr<T>> { template <typename Derived> static std::shared_ptr<T> make() { return std::make_shared<Derived>(); } };
然后实现通用模板工厂函数:
template <typename PTR> PTR create(IDType id) { using trait = creator_trait<PTR>; switch (id) { case kID1: return trait::template make<Type1>(); case kID2: return trait::template make<Type2>(); // 扩展更多类型分支... default: return nullptr; // 或抛出异常处理非法ID } }
使用方式:
auto unique_inst = create<std::unique_ptr<AbstractType>>(kID1); auto shared_inst = create<std::shared_ptr<AbstractType>>(kID2);
方案2:传递创建器对象(灵活适配自定义逻辑)
如果需要支持自定义创建逻辑(比如带参数的构造、自定义内存分配),可以将创建逻辑封装为函数对象,作为参数传入通用工厂:
首先定义创建器:
struct MakeUniqueCreator { template <typename Derived> std::unique_ptr<AbstractType> operator()() { return std::make_unique<Derived>(); } }; struct MakeSharedCreator { template <typename Derived> std::shared_ptr<AbstractType> operator()() { return std::make_shared<Derived>(); } };
然后实现通用工厂:
template <typename PTR, typename Creator> PTR create(IDType id, Creator creator) { switch (id) { case kID1: return creator.template operator()<Type1>(); case kID2: return creator.template operator()<Type2>(); // 扩展更多分支... default: return nullptr; } }
使用方式:
auto unique_inst = create<std::unique_ptr<AbstractType>>(kID1, MakeUniqueCreator{}); auto shared_inst = create<std::shared_ptr<AbstractType>>(kID2, MakeSharedCreator{});
方案3:C++20 概念+编译期分支简化实现
如果使用C++20及以上版本,可以利用std::smart_pointer概念约束输入类型,并通过if constexpr在编译期选择对应的创建函数,无需额外类型特征:
#include <memory> #include <concepts> template <std::smart_pointer PTR> PTR create(IDType id) { using ElementType = typename PTR::element_type; switch (id) { case kID1: if constexpr (std::same_as<PTR, std::unique_ptr<ElementType>>) { return std::make_unique<Type1>(); } else if constexpr (std::same_as<PTR, std::shared_ptr<ElementType>>) { return std::make_shared<Type1>(); } case kID2: if constexpr (std::same_as<PTR, std::unique_ptr<ElementType>>) { return std::make_unique<Type2>(); } else if constexpr (std::same_as<PTR, std::shared_ptr<ElementType>>) { return std::make_shared<Type2>(); } // 扩展更多分支... default: return nullptr; } }
使用方式与方案1一致,直接指定智能指针类型即可。
内容的提问来源于stack exchange,提问作者Patrick Wright
相关产品推荐
相关产品推荐

