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

如何使用std::shared_ptr与std::any实现带类型安全检查的非模板数据容器?

类型安全的任意类型std::shared_ptr容器实现

这是个非常实用的需求——用std::shared_ptr<void>做类型擦除确实能实现存储任意类型,但类型检查的缺失很容易导致运行时错误。结合C++17的特性,我们可以完美解决这个问题,同时完全满足你的所有要求。

方案一:手动维护类型信息(轻量可控)

核心思路是在容器里额外存储一份数据的类型标识,在get()时对比目标类型和存储类型是否一致,不匹配则抛出异常。这里用std::type_index来存储类型信息,开销极小:

#include <memory>
#include <typeindex>
#include <stdexcept>

class Container {
    std::shared_ptr<void> data_;
    std::type_index type_id_;

    // 禁用拷贝/移动,符合你的需求
    Container(const Container&) = delete;
    Container& operator=(const Container&) = delete;
    Container(Container&&) = delete;
    Container& operator=(Container&&) = delete;

public:
    Container() = default;

    // 对外暴露的set接口:直接接收任意shared_ptr<T>,自动记录类型
    template<typename T>
    void set(const std::shared_ptr<T>& ptr) {
        data_ = ptr;
        type_id_ = std::type_index(typeid(T));
    }

    // 对外暴露的get接口:指定目标类型,类型不匹配则抛异常
    template<typename T>
    std::shared_ptr<T> get() {
        if (type_id_ != std::type_index(typeid(T))) {
            throw std::bad_cast(); // 也可以返回nullptr,根据需求调整
        }
        return std::static_pointer_cast<T>(data_);
    }

    // 可选:添加类型检查方法,避免不必要的异常
    template<typename T>
    bool holds_type() const {
        return type_id_ == std::type_index(typeid(T));
    }
};

这个方案的优势是极致轻量,只比原来的std::shared_ptr<void>方案多存储一个std::type_index(本质是整数),完全没有额外的性能开销,同时保证了类型安全。

方案二:用std::any包装std::shared_ptr(简洁易用)

C++17的std::any本身就是为类型安全的类型擦除设计的,我们可以直接用它来存储std::shared_ptr<T>,让std::any帮我们处理类型检查:

#include <memory>
#include <any>
#include <stdexcept>

class Container {
    std::any data_;

    // 禁用拷贝/移动
    Container(const Container&) = delete;
    Container& operator=(const Container&) = delete;
    Container(Container&&) = delete;
    Container& operator=(Container&&) = delete;

public:
    Container() = default;

    template<typename T>
    void set(const std::shared_ptr<T>& ptr) {
        data_ = ptr; // 直接将shared_ptr<T>存入std::any
    }

    template<typename T>
    std::shared_ptr<T> get() {
        try {
            return std::any_cast<std::shared_ptr<T>>(data_);
        } catch (const std::bad_any_cast& e) {
            // 可以重新抛出自定义异常,或者返回nullptr
            throw std::bad_cast();
        }
    }

    template<typename T>
    bool holds_type() const {
        return data_.type() == typeid(std::shared_ptr<T>);
    }
};

关于你补充的疑问:用std::any存std::shared_ptr是不是良好实践?

完全是良好实践,原因如下:

  1. 标准库原生支持:std::any是C++17标准引入的类型安全类型擦除工具,经过充分测试,比手动维护类型信息更可靠,不容易出错。
  2. 无数据拷贝:我们存储的是std::shared_ptr<T>的副本,而不是T本身,所以完全不会拷贝你说的“大体积数据”,只是复制了一个智能指针(开销可以忽略)。
  3. 代码简洁:不需要手动处理类型对比和存储,std::any_cast会自动完成类型检查,代码可读性和可维护性更好。

方案对比与选择

  • 如果你对性能有极致要求(比如嵌入式场景),可以选择手动维护类型信息的方案,它的开销几乎可以忽略。
  • 大多数常规场景下,std::any方案更推荐,因为代码更简洁、更符合现代C++风格,且标准库已经帮我们处理了所有类型安全的细节。

两种方案都严格遵循了你的API设计要求:set()接收任意std::shared_ptr<T>,get<U>()返回std::shared_ptr<U>且类型不匹配时会触发安全检查。

内容的提问来源于stack exchange,提问作者Yuchen Wu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 13:03:11