การใช้สิ่งอันดับใน unordered_map

ฉันต้องการใช้ทูเพิลที่ประกอบด้วย 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;
}

แต่สิ่งนี้ทำให้ฉันมีข้อผิดพลาดดังต่อไปนี้:

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]’

ฉันทำอะไรผิดในเรื่องนี้?


person Xara    schedule 30.12.2013    source แหล่งที่มา


คำตอบ (7)


อาร์กิวเมนต์เทมเพลตสำหรับ 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'));

รหัสถูกดึงมาจาก การใช้ std::tuple เป็นคีย์สำหรับ std: :unordered_map และนี่คือตัวอย่างสด

person Community    schedule 30.12.2013
comment
คุณช่วยอธิบายวิธีที่คุณกำหนด key_hash ได้ไหม หรือกำลังทำอะไรอยู่ใน key_hash? - person Xara; 01.01.2014
comment
@Zara ดูลิงก์สำหรับข้อมูลเพิ่มเติม key_hash ถูกกำหนดไว้ข้างต้น ต่อมาเราใช้มันเป็นหนึ่งในอาร์กิวเมนต์เทมเพลต (โดยที่ unordered_map ต้องใช้แฮช) - person ; 01.01.2014
comment
การทำ XOR ในฟิลด์นั้นเป็นฟังก์ชันแฮชที่แย่มาก กรุณาอย่าใช้รหัสนี้ตามที่เป็นอยู่ ดูเช่น open-std.org/jtc1/sc22/ wg21/docs/papers/2014/n3876.pdf - person jkff; 29.10.2016

ข้อผิดพลาดครั้งแรก:

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) ประการที่สอง tuple ของคุณคาดว่าจะมีตัวอักษร ('c','b') ไม่ใช่สตริง ("c","b")

map[std::make_tuple(1,'c','b')] = 23;
person Benjamin Lindley    schedule 30.12.2013

ตามที่ระบุไว้ 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;                                 
        }                                              

    };
}
person Leo Goodstadt    schedule 29.01.2014

ฉันมีข้อกำหนดของแผนที่แทนที่จะเป็นแผนที่ที่ไม่เรียงลำดับ:
คีย์คือ 3-tuple และ
ค่าคือ 4-tuple

เห็นคำตอบทั้งหมดแล้ว กำลังจะเปลี่ยนเป็นคู่เลย

แต่ด้านล่างนี้ใช้ได้สำหรับฉัน:

// 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 Community 2015 ide

person Manohar Reddy Poreddy    schedule 06.09.2016

สำหรับผู้ที่ใช้ 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;
}
person Daniel    schedule 19.11.2019
comment
ขอบคุณ! โดยวิธีการแฮชการทำงานได้ย้ายไปแล้ว ฉันไม่แน่ใจว่าเมื่อใด แต่ในบูสต์ 1.72 มันอยู่ใน #include <boost/container_hash/extensions.hpp> ฉันไม่แน่ใจว่าทำไมฟังก์ชันแฮชบูสต์สำหรับทูเพิลจึงไม่ได้รับการบันทึกไว้ที่ไหนสักแห่ง ขอขอบคุณอีกครั้งสำหรับคำแนะนำ! - person Phil; 25.04.2020

ต่อไปนี้เป็นวิธีการใช้ tuple เป็นคีย์สำหรับ 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
person edW    schedule 12.09.2018

หลังจากอ่าน บางส่วน อื่นๆ โพสต์ ฉันลงเอยด้วยสิ่งนี้ ใช้อัลกอริธึมการรวมแฮชที่มีประสิทธิภาพ และไม่เชี่ยวชาญสิ่งต่าง ๆ ในเนมสเปซ std คุณจะต้องทำงานเพิ่มเติมหากต้องการให้โค้ดนี้ทำงานกับองค์ประกอบ tuple ขององค์ประกอบที่แฮชได้โดยทั่วไป

ใช้งานได้ใน 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;
person Kyle    schedule 17.07.2019