【问题标题】:Multikey map using variadic templates使用可变参数模板的多键映射
【发布时间】:2013-10-09 20:06:10
【问题描述】:

我正在尝试使用 C++ 中的可变参数模板实现具有不同访问键的映射。我想要的是使这样的语法工作:

MultikeyMap<int, double, float> map1; // int and double are keys, float is value type 
map1[ 2 ] = 3.5;
map1[ 5.7 ] = 22;

MultikeyMap<unsigned long long, int, float, double, int> map2; // more keys, int is value type
map2[100000000000ULL] = 56;

// etc...

我现在的样子:

template<class V, class... Krest>
class MultikeyMap;

template<class V, class K, class... Krest>
class MultikeyMap<V, K, Krest...> : protected std::map<K, V>,
                                    protected MultikeyMap<V, Krest...>
{
public:
    template<class T>
    void insert( const T& t, const V& v )
    {
        std::map<T, V>::insert( make_pair( t, v ));
    }

    template<class T>
    const V* find( const T& k )
    {
        typedef std::map<T,V> currentMap;
        currentMap::const_iterator it = currentMap::find( k );
        return it == currentMap::end() ? 0 : &it->second;
    }

};

template<class V>
class MultikeyMap<V>
{};

我没有在 insert 和 find 中返回迭代器以简化代码。

我发现这个解决方案有两个主要缺陷。

首先,值类型在模板参数列表中排在第一位。最初我试图写

template<class K, class... Krest, class V>
class MultikeyMap<K, Krest..., V>

但编译器坚持“如果类模板部分特化的参数是包扩展,它应该是最后一个参数”

Second 是来自 std::maps 的受保护继承。我真的很想使用合成来代替,但在这种情况下,我看不到访问存储地图的方法。如果有 static_if,我会写

template<class V, class K, class... Krest>
class MultikeyMap<V, K, Krest...> : protected MultikeyMap<V, Krest...>
{
public:
    template<class T>
    void insert( const T& t, const V& v )
    {
        static if( is_same<T,K>::value )
            m_map.insert( make_pair( t, v ));
        else
            MultikeyMap<V, Krest...>::insert( t, v );
    }
private:
    std::map<K,V> m_map;
};

请就我提到的问题提出建议。如果有更好的方法,我会很高兴学习。

感谢阅读。

【问题讨论】:

  • MultikeyMap&lt;unsigned long long, int, int, double, int&gt;: int 被两次引用为关键,这是真的吗?你怎么知道用户是在看第一个还是第二个 int ?另外,我不清楚您要达到什么目的;一个值应该同时出现在所有地图中吗?因为如果它只是在一张地图中,那么拥有一个大的公共容器是没有意义的。
  • @MatthieuM.:谢谢你的观点,应该有一个 int。该值现在应该存在于一个映射中,但稍后我将添加一个功能,为已存储的值添加另一个键。

标签: c++ templates c++11 variadic-templates variadic


【解决方案1】:

更简单但不完全等效的方法可能是Boost.BimapBoost.MultiIndex

前者是一个映射,其中键可以查找值,反之亦然,而后者更通用:它是一个具有任意数量索引的容器,允许排序(“类似列表”),随机 -访问(“vector-like”)、关联(“map-like”)和散列访问。

您可以尝试将可变参数模板包装在 Boost.MultiIndex 周围,然后至少您不必重新实现所有插入/擦除逻辑(但只需简单的包装器)。

注意:Boost.MultiIndex 不需要可变参数类型序列,您还可以拥有一个可变参数序列的成员函数,将用户定义类的各种数据成员提取为主要数据类型。

【讨论】:

    【解决方案2】:

    我会这样做:

    template<class V, class K, class... Krest>
    class MultikeyMap : MultikeyMap<V, Krest...>,
                        MultikeyMap<V, K>
    {
        using ParentMM = MultikeyMap<V, Krest...>;
        using Parent = MultikeyMap<V, K>;
    public:
        using ParentMM::insert;
        using Parent::insert;
    
        using ParentMM::find;
        using Parent::find;
    
        using ParentMM::operator[];
        using Parent::operator[];
    };
    
    template<class V, class K>
    class MultikeyMap<V, K>
    {
        std::map<K, V> k_map;
    public:
        void insert(const K& k, const V& v)
        {
            k_map.insert(std::make_pair(k, v));
        }
    
        const V* find( const K& k ) const
        {
            auto it = k_map.find(k);
            if (it != k_map.end())
                return &it->second;
            return nullptr;
        }
    
        V& operator[](const K& k)
        {
            return k_map[k];
        }
    };
    

    继承在这里似乎很合适,因为它结合了多个实现的行为。我将基数设为私有,因为无论哪种方式都需要 using 声明才能使成员可见。只有基本情况有 std::map 作为成员。

    我不会费心反转模板参数,这与 std::tuple 使用的技巧相同,只需查找您最喜欢的 STL 实现即可。

    编辑

    这里是相同的代码,只是我提到的微不足道的变化,所以键在类型参数中排在第一位:

    template<class Head, class... Tail>
    struct Helper : Helper<Tail...> {
        using Last = typename Helper<Tail...>::Last;
    };
    
    template<class T>
    struct Helper<T> {
        using Last = T;
    };
    
    
    template<class K, class... Rest>
    class MultikeyMap : MultikeyMap<Rest...>,
                        MultikeyMap<K, typename Helper<Rest...>::Last>
    {
        using ParentMM = MultikeyMap<Rest...>;
        using Parent = MultikeyMap<K, typename Helper<Rest...>::Last>;
    
    public:
        using ParentMM::insert;
        using Parent::insert;
    
        using ParentMM::find;
        using Parent::find;
    
        using ParentMM::operator[];
        using Parent::operator[];
    };
    
    template<class K, class V>
    class MultikeyMap<K, V>
    {
        std::map<K, V> k_map;
    public:
        void insert(const K& k, const V& v)
        {
            k_map.insert(std::make_pair(k, v));
        }
    
        const V* find( const K& k ) const
        {
            auto it = k_map.find(k);
            if (it != k_map.end())
                return &it->second;
            return nullptr;
        }
    
        V& operator[](const K& k)
        {
            return k_map[k];
        }
    };
    

    【讨论】:

      猜你喜欢
      • 2023-03-24
      • 2021-11-26
      • 1970-01-01
      • 2020-12-04
      • 2021-10-01
      • 2014-11-26
      • 2016-12-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多