Multi-card using variable templates

I am trying to implement a card with different access keys using variable templates in C ++. I want to get this syntax work:

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... 

Now it looks like this for me:

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

I did not return iterators in the insert and found to make the code simple.

I see two main flaws in this solution.

First , the value type begins first in the template argument list. At first I tried to write

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

but the compiler insists that "if the argument for partial specialization of the template template is a package extension, it should be the last argument .

The second is protected inheritance from std :: maps. I would really like to use the composition instead, but in this case, I see no way to access the saved maps. If static_if existed, I would write

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

I ask for advice on the issues addressed. If there is a better approach, I will be glad to know.

Thank you for reading.

+6
source share
2 answers

Simpler but not quite equivalent approaches are probably Boost.Bimap or Boost.MultiIndex .

The first is a map in which keys can search for values ​​and vice versa, while the latter is much more general: it is a container with an arbitrary number of indices, which allows for ordered ("list-like") access ("vector"), associative ("similar to a map) ") and hashed access.

You can try to wrap your variation patterns around Boost.MultiIndex, then at least you don't have to redefine the whole insert / erase logic (but only thin wrappers).

Note : Boost.MultiIndex does not require a variational sequence of types, you can also have a variational sequence of member functions that retrieve various data elements of a user-defined class as the main data type.

+2
source

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 because it combines the behavior of several implementations. I made the databases private, because the using declaration is required in any case to make the elements visible. Only the base register has std::map as a member.

I will not try to change the template arguments, this is the same trick used for std::tuple , just find your favorite STL implementation.

EDIT

Here is the same code, with the trivial change I mentioned, so the keys first enter parameters like:

 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]; } }; 
+2
source

Source: https://habr.com/ru/post/955168/


All Articles