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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 03:26:09