【问题标题】:How can I make a function that takes either a map or an unordered_map?如何制作一个使用地图或 unordered_map 的函数?
【发布时间】:2014-11-03 04:29:45
【问题描述】:

我正在实现一些基于模板的序列化。我为std::map 实现了模板化函数,但现在我使用的是std::unordered_map。我宁愿不复制和粘贴整个函数,而只是更改参数类型。有什么方法可以制作一个只需要一张地图或一张无序地图的模板吗?

【问题讨论】:

  • 为什么会有这样的限制?如果有人想将您的函数与他们自己的地图实现一起使用,该地图与std::map具有相同的接口怎么办?
  • @Brian:那很好,但我认为我受到序列化框架的限制,无法定义一个接受任何参数的serialize 函数。它不会与其他 serialize 函数发生冲突,比如专门采用整数吗?

标签: c++ templates


【解决方案1】:
template <typename MAP>
void generic_foo(MAP& map)
{
    // generic implementation of your function
    // that works with unordered_map and map

    using K = typename MAP::key_type;
    using T = typename MAP::mapped_type;
}

// matches any possible implementation of std::unorderd_map
template <class Key,                                    
          class T,                                   
          class Hash,                       
          class Pred,                  
          class Alloc>
void foo(std::unordered_map<Key, T, Hash, Pred, Alloc>& m)
{
    // signature matched! forward to your implementation
    generic_foo(m);
}

// matches any possible implementation of std::map        
template <class Key,                                    
          class T,                          
          class Compare,                  
          class Alloc>
void foo(std::map<Key, T, Compare, Alloc>& m)
{
    // signature matched! forward to your implementation
    generic_foo(m);
} 

LIVE DEMO

【讨论】:

  • 但这不是 Paul Evans 的回答?
  • @TomásBadan 是的,但它有一个功能齐全的示例。这是一个更完整的答案。
  • 一个问题:如何从generic_foo 访问键和值类型?我需要将pairs 移出地图
  • @PiotrS.:我指的是类型,而不是值。我不能做std::pair&lt;K, V&gt; item;,因为这些类型不在模板中。我试过MAP::value_type item;,但它说“在‘Map::value_type’之前需要‘typename’,因为‘Map’是一个依赖范围”
  • @Claudiu 然后添加typename...typename MAP::value_type item;
【解决方案2】:

只需重载函数作为非模板函数,一个重载采用std::map,另一个重载采用std::unordered_map。让这两个函数调用一个隐藏的template,它可以接受任何东西,但只能由它们调用。一种方法是将其隐藏在匿名 namespace 中。

【讨论】:

  • @PiotrS。这 复制和粘贴整个函数!那就是被重命名、隐藏和简单地改变以获取任何东西而不是使用 std::map 的函数
  • 采用 std::map 的非模板函数 没有用,因为类型是硬编码的,而 OP 的原始函数是模板。与 map/unorderd_map 中的类型的任何不匹配都会将重载解析为泛型
  • 这是正确的想法,我投了赞成票,但 Piotr 帮助我完成了最终的工作实施
【解决方案3】:
#include<type_traits>
template<typename T>
void foo(T t){
    static_assert(std::is_same<T, std::map</*some_type*/>::value
               || std::is_same<T, std::unordered_map</*some_type*/>::value,
                  "Foo can only get std::map or std::unordered_map.");
}

【讨论】:

    【解决方案4】:

    使用 C++20 概念

    专门支持std::mapstd::unordered_map

    template<typename T> struct is_unordered_map
       : public std::false_type {};
    
    template<typename... Args>
    struct is_unordered_map<std::unordered_map<Args...>>
       : public std::true_type {};
    
    template<typename T> struct is_map
       : public std::false_type {};
    
    template<typename... Args>
    struct is_map<std::map<Args...>>
       : public std::true_type {};
    
    template<typename C>
    concept UnorderedMap = is_unordered_map<C>::value;
    
    template<typename C>
    concept Map = is_map<C>::value;
    
    template<typename C>
    concept MappingContainer =
        Map<C> || UnorderedMap<C>;
    
    void foo(const MappingContainer auto& m) { ... }
    

    代码:https://godbolt.org/z/MrsaGn


    支持任何作为映射类型的类型:

    template<typename C>
    concept MappingContainer = requires(C c) {
        typename C::key_type;
        typename C::mapped_type;
        typename C::value_type;
        typename C::iterator;
        requires std::same_as<decltype(c.begin()), typename C::iterator>;
        requires std::same_as<decltype(c.end()), typename C::iterator>;
        requires std::same_as<
            typename C::value_type,
            std::iter_value_t<typename C::iterator>
        >;
        requires std::same_as<
            typename C::value_type,
            std::pair<const typename C::key_type, typename C::mapped_type>
        >;
    };
    
    void foo(const MappingContainer auto& m) { ... }
    

    代码:https://godbolt.org/z/jPfMhT

    【讨论】:

      【解决方案5】:

      这是一个从std::mapstd::unordered_map 中提取键/值的示例

      template <typename M>
      std::unordered_set<typename M::key_type> GetMapKeys(const M& a_map) {
        std::unordered_set<typename M::key_type> keys;
        for (auto const& e : a_map) {
          keys.emplace(e.first);
        }
        return keys;
      }
      
      template <typename M>
      std::vector<typename M::mapped_type> GetMapValues(const M& a_map) {
        std::vector<typename M::mapped_type> values;
        values.reserve(a_map.size());
        for (auto const& e : a_map) {
          values.push_back(e.second);
        }
        return values;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-11-02
        • 2020-05-14
        • 2019-09-28
        • 1970-01-01
        • 2015-01-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多