我有一个像这样定义和使用的地图
// def.h
struct X{};
struct Y{};
struct myStruct
{
X x;
Y y;
};
typedef std::unordered_map<std::pair<std::string, std::string>, myStruct> myMap;
namespace std
{
template<> struct pair<std::string, std::string>
{
std::string s1,s2;
pair(const std::string& a, const std::string& b):s1(a),s2(b){}
bool operator < (const pair<std::string,std::string>& r)
{
return (0 < r.s1.compare(s1) && (0 < r.s2.compare(s2)));
}
};
}
//use.cpp
class CUse
{
myMap m;
public:
CUse():m(0){}
};
编译器发出的一些错误提取如下
CUse
初始化,注意:请参阅函数模板实例化的参考 &#39; std :: unordered_map,myStruct,std :: hash&lt; _Kty&gt ;,std :: equal_to&lt; _Kty&gt ;,std :: allocator&gt;&gt; :: unordered_map(unsigned __int64)&#39;正在编制
m
声明中注意:请参阅类模板实例化的引用 &#39;的std :: unordered_map,MYSTRUCT,性病::散列&LT; _Kty&GT;,的std :: equal_to&LT; _Kty&GT;,的std ::分配器&GT;&GT;&#39;正在编制
答案 0 :(得分:3)
正如评论中提到的@Bo Persson和@Sean Cline,你需要使用自定义哈希函数/函数来做到这一点。
#include <unordered_map>
#include <string>
#include <tuple>
#include <functional>
#include <cstddef>
#include <iostream>
struct myStruct { int x, y; };
using Key = std::pair<std::string, std::string>;
namespace something
{
struct Compare //custom hash function/functor
{
std::size_t operator()(const Key& string_pair) const
{
// just to demonstrate the comparison.
return std::hash<std::string>{}(string_pair.first) ^
std::hash<std::string>{}(string_pair.second);
}
};
}
using myMap = std::unordered_map<Key, myStruct, something::Compare>;
int main()
{
myMap mp =
{
{ { "name1", "name2" },{ 3,4 } },
{ { "aame1", "name2" },{ 8,4 } },
{ std::make_pair("fame1", "name2"),{ 2,4 } }, // or make pair
{ std::make_pair("fame1", "bame2"),{ 1,2 } }
};
for(const auto& it: mp)
{
std::cout << it.first.first << " " << it.first.second << " "
<< it.second.x << " " << it.second.y << std::endl;
}
return 0;
}
但是,每个对称对都会产生几乎相同的哈希值,这可能会导致, 哈希冲突,从而降低性能。尽管如此,boost.hash
还提供了std::pair
组合哈希的其他专精
另一种解决方案可能是使用std::map<>
。在那里,您还可以为std::pair
指定自定义函数/仿函数,以实现相同的地图结构。即使你不必面对哈希冲突,你也可能不需要这样做。
#include <map>
#include <string>
#include <tuple>
#include <iostream>
struct myStruct { int x, y; };
using Key = std::pair<std::string, std::string>;
namespace something
{
struct Compare
{
bool operator()(const Key& lhs, const Key& rhs) const
{
// do the required comparison here
return std::tie(lhs.first, lhs.second) < std::tie(rhs.first, rhs.second);
}
};
}
using myMap = std::map<Key, myStruct, something::Compare>;
你能告诉我为什么在std中使用我的数据类型不好吗?我的 无论如何,类型都在我自己的程序中定义。
您不应该在std
的命名空间下进行,因为它可能会导致UB。这里给出了可以扩展std
命名空间的明确定义的情境/例外:https://en.cppreference.com/w/cpp/language/extending_std
答案 1 :(得分:1)
回答第二个问题:
谢谢你,但是你能告诉我为什么在std中使用我的数据类型不好吗?我的类型无论如何都是在我自己的程序中定义的
您觉得可以在程序中定义任何内容,对吗? (这至少是你给人的印象。)好吧,C ++实现对namespace std
有同样的感觉 - 它是他们的命名空间,他们可以定义他们想要的东西(当然受C ++标准的限制) )。
如果某个实现需要定义一个(可能是未记录的)辅助函数/ class / whatever,那么期望它可以放在namespace std
中而不会与您的程序冲突。举个例子:如果您的C ++库决定需要为std::pair
定义std::pair<std::string, std::string>
模板的特化,您的程序会发生什么?据我所知,该标准既不要求也不禁止这种专业化,因此它的存在留待执行者自行决定。
存在命名空间以防止命名冲突。特别是,namespace std
的存在是为了将C ++实现细节与用户程序隔离开来。将代码添加到namespace std
会破坏隔离,因此标准会声明它未定义的行为。不要这样做。
(话虽如此,没有什么能阻止你在std::pair<std::string, std::string>
周围编写一个包装类来获得你需要的功能。只需在你自己的命名空间中完成。)
答案 2 :(得分:0)
您需要为您的密钥类型定义std::hash
的特化,如下所示:
#include <unordered_map>
#include <string>
using KeyType = std::pair<std::string, std::string>;
namespace std
{
template<>
struct hash<KeyType>
{
size_t operator()(KeyType const& kt) const
{
size_t hash = 0;
hash_combine(hash, kt.first);
hash_combine(hash, kt.second);
return hash;
}
// taken from boost::hash_combine:
// https://www.boost.org/doc/libs/1_55_0/doc/html/hash/reference.html#boost.hash_combine
template <class T>
inline static void hash_combine(std::size_t& seed, const T& v)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
};
}
int main()
{
std::unordered_map<KeyType, int> us;
return 0;
}