Как объединить std :: unordered_map внутри std :: tuple? - PullRequest
1 голос
/ 06 июля 2019

Я хочу создать класс, который будет иметь std::tuple с std::unordered_map внутри.Я хочу создать метод, который будет объединять карты внутри кортежа.

#include <tuple>
#include <unordered_map>

template <typename ... Ts, std::size_t ... Is>
std::tuple<Ts...> merge_map (std::tuple<Ts...>& t1, std::tuple<Ts...> & t2, std::index_sequence<Is...> const &) { 
    return { 
        (std::get<Is>(t1).merge( std::get<Is>(t2)))... 
    }; 
}

template <class ...Ts>
struct MyContainer
{
    std::tuple<Ts...> data;

    void merge(MyContainer<Ts...>& other){
        data = merge_map(data, other.data, std::make_index_sequence<sizeof...(Ts)>{});
    }

};

int main()
{
    using namespace std;
    unordered_map<string, int> f = {{"zero", 0}, {"one", 1}};
    MyContainer <unordered_map<string, int>> container1 = {.data = f};

    unordered_map<string, int> s = {{"two", 2}, {"three", 3}};
    MyContainer <unordered_map<string, int>> container2 = {.data = s};

    container1.merge(container2);
}

Но я не могу скомпилировать этот код.Я попытался создать простой пример с un std::tuple из int и сделать из них сумму, и это сработало.Но я застрял на этом более сложном примере.Спасибо за любые предложения.

Ответы [ 3 ]

1 голос
/ 06 июля 2019

Большая проблема, которую я вижу, состоит в том, что std::unorderd_map<something...>::merge() return void.

Так что, конечно, не так

return { 
    (std::get<Is>(t1).merge( std::get<Is>(t2)))... 
}; 

Предлагаю изменить merge_map(), используя сворачивание шаблона, следующим образом

template <typename ... Ts, std::size_t ... Is>
std::tuple<Ts...> merge_map (std::tuple<Ts...> & t1,
                             std::tuple<Ts...> & t2,
                             std::index_sequence<Is...> const &) { 
   (std::get<Is>(t1).merge(std::get<Is>(t2)), ...); 

   return t1;
}

Но также не забудьте включить <string> и что синтаксис инициализации {.data = f} не является C ++ 17 (будет доступен начиная с C ++ 20, если я правильно помню).

0 голосов
/ 06 июля 2019

Вот как я это решил.Я также сделал это c ++ 14 дружественным:)

#include <tuple>
#include <unordered_map>



template <typename ... Ts, std::size_t ... Is>
auto merge_map (std::tuple<Ts...>& t1, std::tuple<Ts...> & t2, std::index_sequence<Is...> const &) { 
    return std::make_tuple( 
        std::get<Is>(t1)..., std::get<Is>(t2)...
    ); 
}

template <class ...Ts>
struct MyContainer
{
    std::tuple<Ts...> data;

};

template <class ...Ts>
auto merge(MyContainer<Ts...>& c1, MyContainer<Ts...>& c2){
   return merge_map(c1.data, c2.data, std::make_index_sequence<sizeof...(Ts)>{});
}

int main()
{
    using namespace std;
    unordered_map<string, int> f = {{"zero", 0}, {"one", 1}};
    MyContainer <unordered_map<string, int>> container1 = {.data = std::make_tuple(f)};

    unordered_map<string, int> s = {{"two", 2}, {"three", 3}};
    MyContainer <unordered_map<string, int>> container2 = {.data = std::make_tuple(s)};

    MyContainer<decltype(f), decltype(s)> cc;
    cc.data = merge(container1, container2);

}

0 голосов
/ 06 июля 2019

Попробуйте это:

#include <tuple>
#include <unordered_map>
#include <iostream>

template <typename T>
void merge_map_helper (T& t1, T& t2) { }
template <typename T, std::size_t I, std::size_t ... Is>
void merge_map_helper (T& t1, T& t2) { 
    std::get<I>(t1).merge( std::get<I>(t2));
    merge_map_helper<T, Is...>(t1, t2);
}

template <typename ... Ts, std::size_t ... Is>
void merge_map (std::tuple<Ts...>& t1, std::tuple<Ts...> & t2, std::index_sequence<Is...> const &) { 
    merge_map_helper<std::tuple<Ts...>, Is...>(t1, t2);
}

template <class ...Ts>
struct MyContainer
{
    std::tuple<Ts...> data;

    MyContainer(std::tuple<Ts...> data) : data(std::move(data)) { }

    void merge(MyContainer<Ts...>& other){
        merge_map(data, other.data, std::make_index_sequence<sizeof...(Ts)>{});
    }
};

int main()
{
    using namespace std;
    unordered_map<string, int> f1 = {{"zero1", 0}, {"one", 1}};
    unordered_map<string, int> f2 = {{"zero2", 0}, {"one", 1}};
    MyContainer <unordered_map<string, int>, unordered_map<string, int>> container1 = 
        { { f1, f2 } };

    unordered_map<string, int> s1 = {{"two1", 2}, {"three", 3}};
    unordered_map<string, int> s2 = {{"two2", 2}, {"three", 3}};
    MyContainer <unordered_map<string, int>, unordered_map<string, int>> container2 = 
        { { s1, s2} };

    container1.merge(container2);

    for(auto & k :std::get<0>(container1.data)) {
        std::cout << k.first << " " << k.second << "\n";
    }

    for(auto & k :std::get<1>(container1.data)) {
        std::cout << k.first << " " << k.second << "\n";
    }
}

Обратите внимание, что merge_map не возвращает карту, она изменяет первую. Поэтому я обновил аргументы и возвращаю значения, чтобы отразить это.

...