Search code examples
c++unordered-mapforward-list

Is it possible to insert a forward_list into a unordered_map?


I don't have an interest in reinventing the wheel. I like to keep code very compact and containers are something I like to use so that I don't have to implement everything line by line. So is it possible to use those two containers together?


Solution

  • Obvsiously you can. However, consider Boost Multi-Index.

    Demo

    Live On Coliru

    #include <unordered_map>
    #include <forward_list>
    #include <string>
    
    struct Element {
        int id;
        std::string name;
    
        struct id_equal final : private std::equal_to<int> {
            using std::equal_to<int>::operator();
            bool operator()(Element const& a, Element const& b) const { return (*this)(a.id, b.id); };
        };
        struct name_equal final : private std::equal_to<std::string> {
            using std::equal_to<std::string>::operator();
            bool operator()(Element const& a, Element const& b) const { return (*this)(a.name, b.name); };
        };
        struct id_hash final : private std::hash<int> {
            using std::hash<int>::operator();
            size_t operator()(Element const& el) const { return (*this)(el.id); };
        };
        struct name_hash final : private std::hash<std::string> {
            using std::hash<std::string>::operator();
            size_t operator()(Element const& el) const { return (*this)(el.name); };
        };
    };
    
    int main() {
    
        using namespace std;
        forward_list<Element> const list { { 1, "one" }, { 2, "two" }, { 3, "three" } };
    
        {
            unordered_map<int, Element, Element::id_hash, Element::id_equal> map;
            for (auto& el : list)
                map.emplace(el.id, el);
        }
    
        {
            unordered_map<std::string, Element, Element::name_hash, Element::name_equal> map;
            for (auto& el : list)
                map.emplace(el.name, el);
        }
    }
    

    Demo With Multi-Index

    This achieves the same goal but:

    • in-place (no copies of the container)
    • indexes are always in-synch
    • no manual custom hash/equality function objects

    Live On Coliru

    #include <string>
    #include <iostream>
    #include <boost/multi_index_container.hpp>
    #include <boost/multi_index/hashed_index.hpp>
    #include <boost/multi_index/member.hpp>
    
    struct Element {
        int id;
        std::string name;
    };
    
    namespace bmi = boost::multi_index;
    using Table = bmi::multi_index_container<Element,
          bmi::indexed_by<
                bmi::hashed_unique<bmi::tag<struct by_id>, bmi::member<Element, int, &Element::id> >,
                bmi::hashed_non_unique<bmi::tag<struct by_name>, bmi::member<Element, std::string, &Element::name> >
             >
          >;
    
    int main() {
    
        using namespace std;
        Table const list { { 1, "one" }, { 2, "two" }, { 3, "three" } };
    
        for (auto& el : list.get<by_name>())
            std::cout << el.id << ": " << el.name << "\n";
    
        for (auto& el : list.get<by_id>())
            std::cout << el.id << ": " << el.name << "\n";
    }