模板函数从多个嵌套的unordered_map的值中提取值

时间:2019-08-11 17:09:27

标签: c++ templates recursion variadic-templates

假设我有一个嵌套的std::unordered_map,看起来像这样:

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

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

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)
    {
        ...
    }

2 个答案:

答案 0 :(得分:6)

更简单的解决方案(需要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;
}

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

答案 1 :(得分:4)

  

是否可以使用模板来概括逻辑?一些带有参数包的键。将在每个嵌套容器中进行检查,检查对象是否可用,并在最后将其返回或返回nullptr:

需要一些工作(也许比我更专业的人可以使它更简单),但是请确保有可能。

通过示例...给出了以下自定义类型特征(和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)...))