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

C++模板问题:如何根据类型选择对应容器

可行解决方案

方法1:基于std::type_index + std::any的异构容器

利用C++20/23的std::type_index作为键,std::any存储不同类型的ResourceMap容器,上层用std::unordered_map统一管理。通过模板函数封装容器的获取逻辑,彻底避免逐个类型判断的冗余代码。

核心实现代码

#include <unordered_map>
#include <typeindex>
#include <any>
#include <memory>
#include <stdexcept>

// 定义单资源类型的容器模板
template<typename T>
using ResourceMap = std::unordered_map<std::string, std::shared_ptr<T>>;

class ResourceManager {
private:
    std::unordered_map<std::type_index, std::any> m_resource_containers;

    // 模板函数:获取对应类型的ResourceMap,不存在则自动创建
    template<typename T>
    ResourceMap<T>& get_resource_map() {
        const auto type_idx = std::type_index(typeid(T));
        auto it = m_resource_containers.find(type_idx);
        if (it == m_resource_containers.end()) {
            auto [new_it, success] = m_resource_containers.emplace(
                type_idx, ResourceMap<T>()
            );
            return std::any_cast<ResourceMap<T>&>(new_it->second);
        }
        return std::any_cast<ResourceMap<T>&>(it->second);
    }

public:
    // 加载资源:模板参数指定资源类型
    template<typename T>
    std::shared_ptr<T> Load(const std::string& path) {
        auto& map = get_resource_map<T>();
        // 检查是否已加载
        if (auto it = map.find(path); it != map.end()) {
            return it->second;
        }
        // 实际加载逻辑(示例)
        auto resource = std::make_shared<T>();
        // resource->LoadFromFile(path); // 替换为真实加载代码
        map[path] = resource;
        return resource;
    }

    // 检查资源是否已加载
    template<typename T>
    bool IsResourceLoaded(const std::string& path) {
        try {
            auto& map = get_resource_map<T>();
            return map.contains(path);
        } catch (const std::bad_any_cast&) {
            // 容器不存在,说明从未加载过该类型资源
            return false;
        }
    }
};

优势

  • 新增资源类型无需修改ResourceManager核心逻辑,直接使用模板方法即可,扩展性极强
  • 代码简洁,完全依赖标准库特性实现异构容器管理

方法2:CRTP+静态注册表(零运行时开销)

通过CRTP(奇异递归模板模式)让每个资源类型静态注册自身的容器,上层用std::tuple存储所有类型的容器引用,利用C++23的std::tuple_find在编译时定位对应容器,完全消除运行时类型转换开销。

核心实现代码

#include <tuple>
#include <unordered_map>
#include <memory>
#include <string>

// 前置声明ResourceManager
class ResourceManager;

// CRTP基类,用于静态注册资源容器
template<typename Derived>
class ResourceRegistrar {
protected:
    friend class ResourceManager;
    static inline ResourceMap<Derived> s_resource_map;
};

// 资源示例类型:继承ResourceRegistrar即可完成注册
class Texture : public ResourceRegistrar<Texture> {
    // Texture的具体实现
};

class Model : public ResourceRegistrar<Model> {
    // Model的具体实现
};

// 单资源类型容器模板
template<typename T>
using ResourceMap = std::unordered_map<std::string, std::shared_ptr<T>>;

class ResourceManager {
private:
    // 存储所有注册资源容器的引用元组(C++23支持可变模板推导)
    using ContainerTuple = std::tuple<
        ResourceMap<Texture>&,
        ResourceMap<Model>&
    >;

    ContainerTuple m_containers{
        Texture::s_resource_map,
        Model::s_resource_map
    };

    // 编译时获取对应类型容器的元组索引
    template<typename T>
    ResourceMap<T>& get_resource_map() {
        constexpr auto idx = std::tuple_find<ContainerTuple, ResourceMap<T>&>();
        return std::get<idx>(m_containers);
    }

public:
    template<typename T>
    std::shared_ptr<T> Load(const std::string& path) {
        auto& map = get_resource_map<T>();
        if (auto it = map.find(path); it != map.end()) {
            return it->second;
        }
        auto resource = std::make_shared<T>();
        // resource->LoadFromFile(path); // 替换为真实加载代码
        map[path] = resource;
        return resource;
    }

    template<typename T>
    bool IsResourceLoaded(const std::string& path) {
        auto& map = get_resource_map<T>();
        return map.contains(path);
    }
};

优势

  • 完全编译时推导,无运行时类型转换或哈希查找开销
  • 新增资源类型只需继承ResourceRegistrar并更新ContainerTuple,逻辑清晰可控

方法3:利用C++23的std::variant + 类型访问器

如果资源类型数量固定且有限,可使用std::variant存储所有ResourceMap,配合std::visit和模板访问器实现类型安全的容器操作,避免std::any的运行时转换异常风险。

核心实现代码

#include <variant>
#include <unordered_map>
#include <memory>
#include <string>

// 单资源类型容器模板
template<typename T>
using ResourceMap = std::unordered_map<std::string, std::shared_ptr<T>>;

// 资源示例类型
class Texture {};
class Model {};
class Audio {};

// 定义包含所有资源容器的variant类型
using ResourceContainer = std::variant<
    ResourceMap<Texture>,
    ResourceMap<Model>,
    ResourceMap<Audio>
>;

class ResourceManager {
private:
    std::unordered_map<std::type_index, ResourceContainer> m_containers;

    template<typename T>
    ResourceMap<T>& get_resource_map() {
        const auto type_idx = std::type_index(typeid(T));
        auto& var = m_containers[type_idx];
        // 确保variant存储的是对应类型的容器
        if (!std::holds_alternative<ResourceMap<T>>(var)) {
            var = ResourceMap<T>();
        }
        return std::get<ResourceMap<T>>(var);
    }

public:
    template<typename T>
    std::shared_ptr<T> Load(const std::string& path) {
        auto& map = get_resource_map<T>();
        if (auto it = map.find(path); it != map.end()) {
            return it->second;
        }
        auto resource = std::make_shared<T>();
        // resource->LoadFromFile(path); // 替换为真实加载代码
        map[path] = resource;
        return resource;
    }

    template<typename T>
    bool IsResourceLoaded(const std::string& path) {
        const auto type_idx = std::type_index(typeid(T));
        auto it = m_containers.find(type_idx);
        if (it == m_containers.end()) return false;
        return std::visit([&path](auto& map) {
            using MapType = decltype(map);
            if constexpr (std::is_same_v<MapType, ResourceMap<T>>) {
                return map.contains(path);
            } else {
                return false;
            }
        }, it->second);
    }
};

优势

  • 利用std::variant的类型安全特性,避免std::any的运行时转换异常
  • 适合资源类型数量明确、变动较少的场景

内容的提问来源于stack exchange,提问作者Aleix Rius

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 18:14:50