Multikey map using variadic templates

点点圈 提交于 2019-12-04 06:31:40

Easier but not entirely equivalent approaches are probably Boost.Bimap or Boost.MultiIndex.

The former is a map with where keys can lookup values and vice versa, whereas the latter is much more general: it is a container with an arbitrary number of indices, allowing both sequenced ("list-like"), random-access ("vector-like"), associative ("map-like") and hashed access.

You could try to wrap your variadic templates around Boost.MultiIndex, then at least you don't have to reimplement all the insertion/erasure logic (but only thin wrappers).

Note: Boost.MultiIndex does not require a variadic sequence of types, you can also have a variadic sequence of member functions extracting various data members of a user-defined class as the primary data type.

Here's how I would do it:

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];
    }
};

Inheritance seems appropriate here, as it is combining the behaviour of multiple implementations. I made bases private because the using declaration is required either way to make the members visible. Only the base case has a std::map as a member.

I'm not going to bother reversing the template arguments, it's the same trick used for std::tuple, just look up your favourite STL implementation.

EDIT

Here's the same code, with the trivial change I mentioned, so keys come first in the type parameters:

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];
    }
};
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!