web-dev-qa-db-ja.com

ペアをキーとしてunordered_mapをコンパイルできないのはなぜですか?

unordered_mapを作成して、整数でペアをマッピングしようとしています。

#include <unordered_map>

using namespace std;
using Vote = pair<string, string>;
using Unordered_map = unordered_map<Vote, int>;

Unordered_mapをプライベートメンバーとして宣言したクラスがあります。

ただし、コンパイルしようとすると、次のエラーが表示されます。

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/type_traits:948:38:未定義のテンプレートの暗黙的なインスタンス化 'std :: __ 1 :: hash、std :: __ 1: :basic_string>> '

map<pair<string, string>, int>の代わりにunordered_mapのような通常のマップを使用すると、このエラーは発生しません。

pairを順不同マップのキーとして使用することはできませんか?

43
Mapplet

キータイプに適したハッシュ関数を提供する必要があります。簡単な例:

#include <unordered_map>
#include <functional>
#include <string>
#include <utility>

// Only for pairs of std::hash-able types for simplicity.
// You can of course template this struct to allow other hash functions
struct pair_hash {
    template <class T1, class T2>
    std::size_t operator () (const std::pair<T1,T2> &p) const {
        auto h1 = std::hash<T1>{}(p.first);
        auto h2 = std::hash<T2>{}(p.second);

        // Mainly for demonstration purposes, i.e. works but is overly simple
        // In the real world, use sth. like boost.hash_combine
        return h1 ^ h2;  
    }
};

using Vote = std::pair<std::string, std::string>;
using Unordered_map = std::unordered_map<Vote, int, pair_hash>;

int main() {
    Unordered_map um;
}

これは機能しますが、最高のハッシュプロパティがありませんboost.hash_combine のようなものを見たいと思うかもしれません。

現実世界での使用:Boostは、関数セット hash_value も提供します。これは、std::pair、およびstd::Tupleおよびほとんどの標準コンテナーのハッシュ関数を既に提供しています。


より正確には、衝突が多すぎます。たとえば、すべての対称ペアは0にハッシュされ、置換のみが異なるペアは同じハッシュを持ちます。これはおそらくプログラミングの演習には適していますが、実際のコードのパフォーマンスを著しく損なう可能性があります。

55
Baum mit Augen

この問題を解決する好ましい方法は、ペアを一意の整数(またはハッシュ可能なデータ型)に変換するkey関数を定義することです。このキーはハッシュキーではありません。 unordered_mapによって最適にハッシュされるのは、データのペアの一意のIDです。たとえば、タイプのunordered_mapを定義したい場合

  unordered_map<pair<int,int>,double> Map;

また、Map[make_pair(i,j)]=valueまたはMap.find(make_pair(i,j))を使用してマップを操作したい場合。次に、整数のペアmake_pair(i,j)をハッシュする方法をシステムに指示する必要があります。その代わりに、定義できます

  inline size_t key(int i,int j) {return (size_t) i << 32 | (unsigned int) j;}

次に、マップのタイプを

  unordered_map<size_t,double> Map;

これで、Map[key(i,j)]=valueまたはMap.find(key(i,j))を使用してマップを操作できます。すべてのmake_pairは、インラインkey関数を呼び出すようになりました。

ハッシュ部分はシステムによって行われるため、この方法はキーが最適にハッシュされることを保証します。システムは常にすべてのバケットが等しくなる可能性を確保するためにプライムになる内部ハッシュテーブルサイズを選択します。ただし、keyがすべてのペアに対して一意であることを100%確認する必要があります。つまり、2つの異なるペアが同じキーを持つことはできません。

9
Zhuoran He

ペアキーの場合、ブーストペアハッシュ関数を使用できます。

#include <iostream>
#include <boost/functional/hash.hpp>
#include <unordered_map>
using namespace std;

int main() {
  unordered_map<pair<string, string>, int, boost::hash<pair<string, string>>> m;

  m[make_pair("123", "456")] = 1;
  cout << m[make_pair("123", "456")] << endl;
  return 0;
}

同様に、ベクトルに対してブーストハッシュを使用できます。

#include <iostream>
#include <boost/functional/hash.hpp>
#include <unordered_map>
#include <vector>
using namespace std;

int main() {
  unordered_map<vector<string>, int, boost::hash<vector<string>>> m;
  vector<string> a({"123", "456"});

  m[a] = 1;
  cout << m[a] << endl;
  return 0;
}
7
Felix Guo

コンパイルエラーが示すように、std名前空間にはstd::hash<std::pair<std::string, std::string>>の有効なインスタンス化はありません。

私のコンパイラによると:

エラーC2338 C++標準は、このタイプのハッシュを提供しません。 c:\ program files(x86)\ Microsoft visual studio 14.0\vc\include\xstddef 381

次のように、std::hash<Vote>に独自の専門化を提供できます。

#include <string>
#include <unordered_map>
#include <functional>

using namespace std;
using Vote = pair<string, string>;
using Unordered_map = unordered_map<Vote, int>;

namespace std
{
    template<>
    struct hash<Vote>
    {
        size_t operator()(Vote const& v) const
        {
            // ... hash function here ...
        }
    };
}

int main()
{
    Unordered_map m;
}
3
bku_drytt

pairを使用することが厳密な要件ではない場合、単純に2回マップを使用できます。

#include <unordered_map>

using namespace std;
using Unordered_map = unordered_map<string, unordered_map<string, int>>;

Unordered_map um;
um["Region1"]["Candidate1"] = 10;
cout << um["Region1"]["Candidate1"];    // 10
2
Efreeto

answer byBaum mit Augen、ユーザーJoe Black例が求められます ハッシュ関数を定義する代わりに lambda式 を使用する場合。 意見 ofBaum mit Augen、これは読みやすさを損なう可能性があることに同意します。解決。したがって、OPが提示するstd::pair<std::string, std::string>の特定のソリューションに焦点を当てて、例を短くしたいと思います。この例では、std::hash<std::string>関数呼び出しの handcrafted の組み合わせも使用しています。

using Vote = std::pair<std::string, std::string>;
auto hash = [](const Vote& v){
    return std::hash<std::string>()(v.first) * 31 + std::hash<std::string>()(v.second);
};
using Unordered_map = std::unordered_map<Vote, int, decltype(hash)>;
Unordered_map um(8, hash);

イデオンのコード

0
honk

私はこれが他の答えと比べてあまりにも素朴な実装であることを知っていますが、回避策があります。

ペアの入力を取得する場合は、入力を取得しながらunordered_hashの別の整数でペアをハッシュし、この整数値を間接的に使用してペアをハッシュします。

unordered_hash<int, Vote> h;
using Unordered_map = unordered_map<i, int>; // i is corresponding value to pair
0
Gaurav Pant