作为std中定义的unordered_map的自定义键的std ::字符串对失败,模板错误

时间:2018-05-31 12:11:29

标签: c++ c++11 hash compare stdmap

我有一个像这样定义和使用的地图

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

编译器发出的一些错误提取如下

  1. 在构造函数CUse初始化,
  2.   

    注意:请参阅函数模板实例化的参考   &#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;正在编制

    1. 在CUse的m声明中
    2.   

      注意:请参阅类模板实例化的引用   &#39;的std :: unordered_map,MYSTRUCT,性病::散列&LT; _Kty&GT;,的std :: equal_to&LT; _Kty&GT;,的std ::分配器&GT;&GT;&#39;正在编制

3 个答案:

答案 0 :(得分:3)

正如评论中提到的@Bo Persson和@Sean Cline,你需要使用自定义哈希函数/函数来做到这一点。

LIVE DEMO

#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指定自定义函数/仿函数,以实现相同的地图结构。即使你不必面对哈希冲突,你也可能不需要这样做。

LIVE DEMO

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