![](/img/trans.png)
[英]Why a default constructor is needed using unordered_map and tuple?
[英]Using tuple in unordered_map
我想在unordered_map
使用由int
、 char
、 char
组成的元组。 我这样做:
#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;
}
但这给了我以下错误:
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]’
我在这做错了什么?
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;
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
最后,正如 Benjamin Lindley 已经回答的那样,您需要使用std::make_tuple
:
// d is data
m[std::make_tuple(1, 'a', 'b')] = d;
auto itr = m.find(std::make_tuple(1, 'a', 'b'));
代码是从Using a std::tuple as key for std::unordered_map 中获取的,这里是Live Example 。
第一个错误:
map.cpp:9:21: error: expected a type, got ‘kk’
正如错误明确指出的那样,模板参数需要是一个类型。 kk
不是一个类型,它是一个对象。 也许您打算将其设为 typedef?
typedef tuple <int,char,char> kk;
unordered_map<kk,int> map;
第二个错误:
map[1,"c","b"]=23;
这里有两个问题。 首先,在值之间放置逗号不会将它们变成一个元组。 您需要明确说明它,要么调用元组类型的构造函数,要么使用返回元组的函数(例如std::make_tuple
)。 其次,您的元组需要字符( 'c','b'
),而不是字符串( "c","b"
)。
map[std::make_tuple(1,'c','b')] = 23;
正如所指出的, 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;
}
};
}
我需要地图而不是无序地图:
键是三元组和
值是一个 4 元组
看到所有的答案,我正要换成对
但是,以下对我有用:
// declare a map called map1
map <
tuple<short, short, short>,
tuple<short, short, short, short>
> map1;
// insert an element into map1
map1[make_tuple(1, 1, 1)] = make_tuple(0, 0, 1, 1);
// this also worked
map1[{1, 1, 1}] = { 0, 0, 1, 1 };
我正在使用 Visual Studio 社区 2015 ide
对于那些使用boost
他们可以使用这个重新路由散列到 boost 的实现
#include "boost/functional/hash.hpp"
#include <string>
#include <unordered_map>
#include <cstring>
#include <iostream>
#include <tuple>
using Key = std::tuple<int, char, char>;
struct KeyHash {
std::size_t operator()(const Key & key) const
{
return boost::hash_value(key);
}
};
using Map = std::unordered_map<Key, int, KeyHash>;
int main()
{
Map map;
map[1,"c","b"] = 23;
return 0;
}
这是一种使用元组作为 unordered_map 的键而不使用哈希特化的方法:
#include <string>
#include <tuple>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <vector>
#include <unordered_map>
using namespace std;
string fToStr(unordered_map<double,int>& dToI,float x)
{
static int keyVal=0;
stringstream ss;
auto iter = dToI.find(x);
if(iter == dToI.end()) {
dToI[x]=++keyVal;
ss << keyVal;
} else {
ss << iter->second;
}
return ss.str();
}
typedef tuple<int,char,char> TICC;
const char ReservedChar=',';
string getKey(TICC& t)
{
stringstream ss;
ss << get<0>(t) << ReservedChar << get<1>(t) << ReservedChar << get<2>(t);
return ss.str();
}
int main()
{
unordered_map< string,TICC > tupleMp;
vector<TICC> ticc={make_tuple(1, 'a', 'b'),make_tuple(1, 'b', 'c'),make_tuple(2, 'a', 'b')};
for(auto t : ticc)
tupleMp[getKey(t)]=t;
for(auto t : ticc) {
string key = getKey(t);
auto val = tupleMp[key];
cout << "tupleMp[" << key << "]={" << get<0>(val) << "," << get<1>(val) << ","<< get<2>(val) << "} ";
}
cout << endl;
//for float tuple elements use a second float to int key map
unordered_map< double,int > dToI;
vector<float> v{1.234,1.234001,1.234001};
cout << "\nfloat keys: ";
for(float f : v)
cout << setprecision(7) << f << "=" << fToStr(dToI,f) << " ";
cout << endl;
return 0;
}
输出是:
tupleMp[1,a,b]={1,a,b} tupleMp[1,b,c]={1,b,c} tupleMp[2,a,b]={2,a,b}
float keys: 1.234=1 1.234001=2 1.234001=2
阅读后几个其他职位我结束了这一点。 它使用高效的散列组合算法,并且不会专门处理std
命名空间中的内容。 如果您希望此代码通常适用于任何可散列元素的元组,则需要做更多的工作。
这适用于 C++11 及更高版本。 在 C++03 中,您可以使用boost::hash
而不是std::hash
。
typedef tuple<int, char, char> MyTuple;
// define a hash function for this tuple
struct KeyHash : public std::unary_function<MyTuple, std::size_t> {
std::size_t operator()(const MyTuple& k) const {
// the magic operation below makes collisions less likely than just the standard XOR
std::size_t seed = std::hash<int>()(std::get<0>(k));
seed ^= std::hash<char>()(std::get<1>(k)) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed ^ (std::hash<char>()(std::get<2>(k)) + 0x9e3779b9 + (seed << 6) + (seed >> 2));
}
};
// define the comparison operator for this tuple
struct KeyEqual : public std::binary_function<MyTuple, MyTuple, bool> {
bool operator()(const MyTuple& v0, const MyTuple& v1) const {
return (std::get<0>(v0) == std::get<0>(v1) && std::get<1>(v0) == std::get<1>(v1) &&
std::get<2>(v0) == std::get<2>(v1));
}
};
typedef unordered_map<MyTuple, int, KeyHash, KeyEqual> MyMap;
使用 std::integer_sequence 可以帮助:
struct hash_tuple {
template <std::size_t...Index>
size_t recursive_hash(const auto &x) const{
return (boost::get<Index>(x) ^ ... );
}
template <template <typename> class Ts,typename...Args>
size_t operator()(const Ts<Args...>& x) const{
return recursive_hash<std::make_integer_sequence<int,sizeof...(Args)>>(x);
}
};
using Map = std::unordered_map<Key, int, hash_tuple>;
此代码适用于所有用作键的元组
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.