【问题标题】:Template function to extract value out of several nested unordered_map's从几个嵌套的 unordered_map 中提取值的模板函数
【发布时间】:2019-12-18 11:47:49
【问题描述】:

假设我有一个嵌套的std::unordered_map,如下所示:

std::unordered_map<ResourceName, std::unordered_map<HAL::ResourceFormat::Color, HAL::RTDescriptor>>

我想要一个函数,该函数将基于两个键 ResourceNameHAL::ResourceFormat::Color 如果对象存在或 nullptr 否则返回指向 HAL::RTDescriptor 的指针。简单的实现如下所示:

const HAL::RTDescriptor* ResourceDescriptorStorage::GetRTDescriptor(ResourceName resourceName, HAL::ResourceFormat::Color format) const
    {
        auto mapIt = mRTDescriptorMap.find(resourceName);

        if (mapIt == mRTDescriptorMap.end()) {
            return nullptr;
        }

        auto& nestedMap = mapIt->second;
        auto nestedMapIt = nestedMap.find(format);

        if (nestedMapIt == nestedMap.end()) {
            return nullptr;
        }

        return &nestedMapIt->second;
    }

有没有办法使用模板来概括逻辑? 带有键参数包的东西。将通过每个嵌套容器的东西,检查对象可用性并在最后返回它或nullptr

template<
        template<class...> class AssociativeContainer,
        class... Keys
    >
        decltype(auto) Find(const AssociativeContainer<...>& rootContainer, Keys&&... keys)
    {
        ...
    }

【问题讨论】:

    标签: c++ templates recursion variadic-templates


    【解决方案1】:

    更简单的解决方案(需要 C++17):

    template<class AssociativeContainer, class Key, class... Keys>
    auto Find(const AssociativeContainer& container, Key&& key, Keys&&... keys){
        auto it = container.find(std::forward<Key>(key));
        bool found = it != container.end();
        if constexpr(sizeof...(Keys) == 0)
            return found ? &it->second : nullptr;
        else
            return found ? Find(it->second, std::forward<Keys>(keys)...) : nullptr;
    }
    

    这也允许获取对任何中间容器的引用,因为它不需要传递所有键。

    【讨论】:

      【解决方案2】:

      有没有办法使用模板来概括逻辑?带有键参数包的东西。将通过每个嵌套容器,检查对象可用性并在最后返回它或 nullptr 的东西:

      需要一点工作(也许比我更专业的人可以让它更简单),但肯定有可能。

      举个例子……给定一个自定义类型的traits(和using以简化使用)如下

      template <typename T>
      struct lastType
       { using type = T; };
      
      template <template <typename...> class C, typename K, typename V>
      struct lastType<C<K, V>> : public lastType<V>
       { };
      
      template <typename T>
      using lastType_t = typename lastType<T>::type;
      

      你可以递归写Find()如下

      // ground case
      template <typename V>
      V const * Find (V const & val)
       { return &val; }
      
      // recursion case
      template <typename C, typename K0, typename ... Ks>
      lastType_t<C> const * Find (C const & cnt, K0 && key0, Ks && ... keys)
       {
         auto mapIt = cnt.find(std::forward<K0>(key0));
      
         if ( mapIt == cnt.cend() ) 
            return nullptr;
      
         return Find(mapIt->second, std::forward<Ks>(keys)...);
       }
      

      以下是完整的编译示例

      #include <map>
      #include <string>
      #include <iostream>
      #include <unordered_map>
      
      template <typename T>
      struct lastType
       { using type = T; };
      
      template <template <typename...> class C, typename K, typename V>
      struct lastType<C<K, V>> : public lastType<V>
       { };
      
      template <typename T>
      using lastType_t = typename lastType<T>::type;
      
      template <typename V>
      V const * Find (V const & val)
       { return &val; }
      
      template <typename C, typename K0, typename ... Ks>
      lastType_t<C> const * Find (C const & cnt, K0 && key0, Ks && ... keys)
       {
         auto mapIt = cnt.find(std::forward<K0>(key0));
      
         if ( mapIt == cnt.cend() ) 
            return nullptr;
      
         return Find(mapIt->second, std::forward<Ks>(keys)...);
       }
      
      using typeC = std::map<int,
                       std::unordered_map<std::string,
                          std::unordered_map<long,
                             std::map<char, long long>>>>;
      
      int main ()
       {
         typeC c;
      
         c[0]["one"][2l]['3'] = 4ll;
      
         auto v = Find(c, 0, "one", 2l, '3');
      
         std::cout << (*v) << std::endl;
      
         static_assert( std::is_same_v<decltype(v), long long const *>, "!" );
       }
      

      -- 编辑--

      我今天特别笨:正如 krisz 在他的回答中强调的那样(谢谢),三元运算符允许使用 auto 作为返回类型(来自 C++14)。

      所以不需要lastType自定义类型特征,Find()可以简单写成

      // ground case
      template <typename V>
      V const * Find (V const & val)
       { return &val; }
      
      // recursion case
      template <typename C, typename K0, typename ... Ks>
      auto Find (C const & cnt, K0 && key0, Ks && ... keys)
       {
         auto mapIt = cnt.find(std::forward<K0>(key0));
      
         return mapIt == cnt.cend()
            ? nullptr
            : Find(mapIt->second, std::forward<Ks>(keys)...);
       }
      

      对于 C++11,递归情况还需要尾随返回类型;举例

      -> decltype(Find(cnt.find(std::forward<K0>(key0))->second, std::forward<Ks>(keys)...))
      

      【讨论】:

        猜你喜欢
        • 2015-03-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-17
        • 2010-10-05
        相关资源
        最近更新 更多