C++11中借助std::any传递shared_ptr的实现难题求助
嘿,这个问题我太熟了——C++里虚模板函数确实是个绕不开的坑,但咱们有两种非常贴合你场景的解决方案,结合你提到的protobuf发布者/消费者的需求,给你详细拆解下:
方案一:CRTP(奇异递归模板模式)——编译期强制实现,零运行时开销
这个方案完美适配「每个派生类对应固定类型的shared_ptr,基类要确保派生类实现处理函数」的需求,而且是编译期检查,没有虚函数的运行时开销。
核心思路是让基类作为模板,派生类继承时传入自己的类型和目标shared_ptr类型,基类通过静态断言强制派生类实现指定的处理函数:
#include <memory> #include <type_traits> // CRTP基类模板,强制派生类实现特定处理函数 template <typename Derived, typename SharedPtrType> class BaseHandler { public: // 对外统一的调用接口,内部转发给派生类的实现 void handle_shared_ptr(SharedPtrType ptr) { // 编译期检查:确保Derived类必须实现handle_impl成员函数 static_assert( std::is_same_v<decltype(&Derived::handle_impl), void (Derived::*)(SharedPtrType)>, "Error: Derived class must implement 'void handle_impl(SharedPtrType)'!" ); // 转发给派生类的具体实现 static_cast<Derived*>(this)->handle_impl(std::move(ptr)); } protected: // 保护构造函数,禁止直接实例化基类 BaseHandler() = default; };
派生类示例(处理protobuf发布者的shared_ptr)
假设你有一个ProtobufPublisher类,派生类可以这样实现:
#include <memory> // 假设的protobuf发布者类 class ProtobufPublisher { public: void publish(const std::string& msg) { /* 发布逻辑 */ } }; // 派生类:指定自己的类型和目标shared_ptr类型 class PublisherHandler : public BaseHandler<PublisherHandler, std::shared_ptr<ProtobufPublisher>> { public: // 必须实现的具体处理函数,基类会强制检查 void handle_impl(std::shared_ptr<ProtobufPublisher> pub) { // 这里写你的自定义操作,比如调用发布接口 pub->publish("Hello from PublisherHandler!"); } };
这个方案的优势:
- 编译期就会检查派生类是否实现了要求的函数,不用等到运行时才发现问题
- 没有虚函数的运行时开销,性能拉满
- 完全贴合「每个派生类对应固定
shared_ptr类型」的场景
方案二:类型擦除+纯虚接口——支持统一基类,动态处理多种类型
如果你的场景需要把不同类型的handler放到同一个容器里统一管理(比如需要动态调度不同的protobuf处理逻辑),可以用这个方案。核心是用std::any做类型擦除,让非模板的基类提供统一接口,模板派生类适配特定的shared_ptr类型:
#include <memory> #include <any> #include <functional> #include <stdexcept> // 非模板基类,提供统一的处理接口 class AnyHandler { public: virtual ~AnyHandler() = default; // 通用处理接口,接受任意类型的参数(用std::any包装) virtual void handle(const std::any& ptr) = 0; }; // 模板派生类,适配特定的shared_ptr类型 template <typename SharedPtrType> class SpecificHandler : public AnyHandler { public: using HandlerFunc = std::function<void(SharedPtrType)>; // 构造时传入自定义处理逻辑 explicit SpecificHandler(HandlerFunc func) : m_handler_func(std::move(func)) {} void handle(const std::any& ptr) override { try { // 把std::any转换回目标shared_ptr类型 auto typed_ptr = std::any_cast<SharedPtrType>(ptr); // 执行自定义操作 m_handler_func(std::move(typed_ptr)); } catch (const std::bad_any_cast& e) { // 处理类型不匹配的情况,比如抛异常或打日志 throw std::runtime_error("Mismatched shared_ptr type in handler: " + std::string(e.what())); } } private: HandlerFunc m_handler_func; };
使用示例(处理protobuf消费者的shared_ptr)
// 假设的protobuf消费者类 class ProtobufConsumer { public: void subscribe() { /* 订阅逻辑 */ } }; int main() { // 创建处理ProtobufConsumer的handler,传入自定义逻辑 auto consumer_handler = std::make_shared<SpecificHandler<std::shared_ptr<ProtobufConsumer>>>( [](std::shared_ptr<ProtobufConsumer> cons) { cons->subscribe(); // 其他自定义操作... } ); // 可以把不同类型的handler放到同一个容器里统一管理 std::vector<std::shared_ptr<AnyHandler>> handlers; handlers.push_back(consumer_handler); // 调用处理逻辑 auto cons_ptr = std::make_shared<ProtobufConsumer>(); handlers[0]->handle(cons_ptr); }
这个方案的优势:
- 支持统一的基类类型,可以将不同的handler放到容器里动态调度
- 灵活度高,不需要提前确定所有可能的
shared_ptr类型 - 缺点是有
std::any_cast的运行时开销,需要处理类型不匹配的异常
总结一下:如果是让其他开发者创建派生类对应固定的protobuf发布/订阅者类型,CRTP方案绝对是首选;如果需要动态管理多种类型的handler,就用类型擦除方案。
内容的提问来源于stack exchange,提问作者user3851038
相关产品推荐
相关产品推荐

