如何在 std::tuple 中合并 std::unordered_map?

Posted

技术标签:

【中文标题】如何在 std::tuple 中合并 std::unordered_map?【英文标题】:How to merge std::unordered_map inside std::tuple? 【发布时间】:2019-07-06 15:57:01 【问题描述】:

我想创建一个类,里面有一个std::tuplestd::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);

但我无法编译这段代码。 我试图用 int 的 un std::tuple 创建一个简单的例子,然后对它们求和,它起作用了。但我坚持这个更复杂的例子。 感谢您的任何建议。

【问题讨论】:

【参考方案1】:

我看到的更大的问题是std::unorderd_map&lt;something...&gt;::merge() 返回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;

但还要记住包括 &lt;string&gt; 并且 .data = f 初始化语法不是 C++17(如果我没记错的话,将从 C++20 开始提供)。

【讨论】:

伙计,我不知道在这种情况下如何使用..., ...) 语法纯属疯狂。天才,但疯狂。 @RadosławCybulski - 永远不要低估黑暗的力量......嗯嗯......对不起......可变参数模板。【参考方案2】:

试试这个:

#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 不返回地图,它会修改第一个。所以我更新了参数和返回值来反映它。

【讨论】:

【参考方案3】:

我就是这样解决的。我还让它对 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);



【讨论】:

以上是关于如何在 std::tuple 中合并 std::unordered_map?的主要内容,如果未能解决你的问题,请参考以下文章

在std :: tuple中转换元素

C++ 中的 std::pair 和 std::tuple

C++ 中的 std::pair 和 std::tuple

将 std::tuple 的类型转换为 std::pair

std::tuple

插入 std::set<std::tuple<std::string, ...>> 时重复