在unordered_map中使用元组

Xar*_*ara 14 c++ unordered-map hashmap stdtuple

我想使用的元组组成的int,char,char在我的unordered_map.我这样做:

#include <string>
#include <unordered_map>
#include <cstring>
#include <iostream>
#include <tuple>

using namespace std;

tuple <int,char,char> kk;
unordered_map<kk,int> map;

int main()
{
    map[1,"c","b"]=23;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

但这给了我以下错误:

map.cpp:9:21: error: type/value mismatch at argument 1 in template parameter list     for ‘template<class _Key, class _Tp, class _Hash, class _Pred, class _Alloc> class    std::unordered_map’
map.cpp:9:21: error:   expected a type, got ‘kk’
map.cpp:9:21: error: template argument 3 is invalid
map.cpp:9:21: error: template argument 4 is invalid
map.cpp:9:21: error: template argument 5 is invalid
map.cpp:9:26: error: invalid type in declaration before ‘;’ token
map.cpp: In function ‘int main()’:
map.cpp:14:16: error: assignment of read-only location ‘"b"[map]’
Run Code Online (Sandbox Code Playgroud)

我在这做错了什么?

小智 16

unordered_map的模板参数如下所示:

template<

    class Key,
    class T,
    class Hash = std::hash<Key>,
    class KeyEqual = std::equal_to<Key>,
    class Allocator = std::allocator< std::pair<const Key, T> >
> class unordered_map;
Run Code Online (Sandbox Code Playgroud)

std::hash不是专门为元组(向下滚动到标准专业化的库类型).因此,您需要提供自己的,如下所示:

typedef std::tuple<int, char, char> key_t;

struct key_hash : public std::unary_function<key_t, std::size_t>
{
 std::size_t operator()(const key_t& k) const
 {
   return std::get<0>(k) ^ std::get<1>(k) ^ std::get<2>(k);
 }
};
// ..snip..
typedef std::unordered_map<const key_t,data,key_hash,key_equal> map_t;
//                                             ^ this is our custom hash
Run Code Online (Sandbox Code Playgroud)

最后,正如本杰明林德利回答已经解决的那样,你需要使用std::make_tuple:

// d is data
m[std::make_tuple(1, 'a', 'b')] = d;
auto itr = m.find(std::make_tuple(1, 'a', 'b'));
Run Code Online (Sandbox Code Playgroud)

代码是从使用std :: tuple作为std :: unordered_map的键来获取的,这里是Live Example.

  • 对字段进行异或是一个糟糕的哈希函数。请不要按原样使用此代码。参见例如http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf (2认同)

Ben*_*ley 13

第一个错误:

map.cpp:9:21: error:   expected a type, got ‘kk’
Run Code Online (Sandbox Code Playgroud)

正如错误清楚地说明的那样,模板参数需要是一种类型.kk不是一种类型,它是一个对象.也许你想让它成为一个typedef?

typedef tuple <int,char,char> kk;
unordered_map<kk,int> map;
Run Code Online (Sandbox Code Playgroud)

第二个错误:

map[1,"c","b"]=23;
Run Code Online (Sandbox Code Playgroud)

这里有两个问题.首先,在值之间加上逗号不会产生元组.您需要明确它,要么调用元组类型的构造函数,要么使用返回元组的函数(例如std::make_tuple).其次,你的元组期待chars('c','b'),而不是字符串("c","b").

map[std::make_tuple(1,'c','b')] = 23;
Run Code Online (Sandbox Code Playgroud)


Leo*_*adt 7

正如所指出的,std :: hash不是专门用于元组的.但是,如果你的元组包含像string和int这样的标准可扩展类型,那么来自generic-hash-for-tuples-in-unordered-map-unordered-set的以下代码将自动在c ++ 11中添加这样的支持.

只需将代码粘贴到头文件中,并在需要时包含它:

#include <tuple>
// function has to live in the std namespace 
// so that it is picked up by argument-dependent name lookup (ADL).
namespace std{
    namespace
    {

        // Code from boost
        // Reciprocal of the golden ratio helps spread entropy
        //     and handles duplicates.
        // See Mike Seymour in magic-numbers-in-boosthash-combine:
        //     https://stackoverflow.com/questions/4948780

        template <class T>
        inline void hash_combine(std::size_t& seed, T const& v)
        {
            seed ^= hash<T>()(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
        }

        // Recursive template code derived from Matthieu M.
        template <class Tuple, size_t Index = std::tuple_size<Tuple>::value - 1>
        struct HashValueImpl
        {
          static void apply(size_t& seed, Tuple const& tuple)
          {
            HashValueImpl<Tuple, Index-1>::apply(seed, tuple);
            hash_combine(seed, get<Index>(tuple));
          }
        };

        template <class Tuple>
        struct HashValueImpl<Tuple,0>
        {
          static void apply(size_t& seed, Tuple const& tuple)
          {
            hash_combine(seed, get<0>(tuple));
          }
        };
    }

    template <typename ... TT>
    struct hash<std::tuple<TT...>> 
    {
        size_t
        operator()(std::tuple<TT...> const& tt) const
        {                                              
            size_t seed = 0;                             
            HashValueImpl<std::tuple<TT...> >::apply(seed, tt);    
            return seed;                                 
        }                                              

    };
}
Run Code Online (Sandbox Code Playgroud)