从多个嵌套 unordered_map 中提取值的模板函数
Template function to extract value out of several nested unordered_map's
假设我有一个嵌套的 std::unordered_map
,如下所示:
std::unordered_map<ResourceName, std::unordered_map<HAL::ResourceFormat::Color, HAL::RTDescriptor>>
我想要一个函数,它将 return 一个指向 HAL::RTDescriptor
的指针,基于两个键 ResourceName
和 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;
}
有没有办法使用模板来概括逻辑?
带有键参数包的东西。将通过每个嵌套容器的东西,检查对象可用性和 return 它或最后的 nullptr
:
template<
template<class...> class AssociativeContainer,
class... Keys
>
decltype(auto) Find(const AssociativeContainer<...>& rootContainer, Keys&&... keys)
{
...
}
Is there a way to use templates to generalize the logic? Something with parameter packs for keys. Something that will go through each nested container, check for object availability and return it or nullptr at the end:
需要一些工作(也许比我更专业的人可以使它更简单)但肯定是可能的。
举例...给定一个自定义类型特征(和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
用作 return 类型(来自 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,递归情况还需要尾部 return 类型;举个例子
-> decltype(Find(cnt.find(std::forward<K0>(key0))->second, std::forward<Ks>(keys)...))
更简单的解决方案(需要 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;
}
这也允许获取对任何中间容器的引用,因为它不需要传递所有键。
假设我有一个嵌套的 std::unordered_map
,如下所示:
std::unordered_map<ResourceName, std::unordered_map<HAL::ResourceFormat::Color, HAL::RTDescriptor>>
我想要一个函数,它将 return 一个指向 HAL::RTDescriptor
的指针,基于两个键 ResourceName
和 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;
}
有没有办法使用模板来概括逻辑?
带有键参数包的东西。将通过每个嵌套容器的东西,检查对象可用性和 return 它或最后的 nullptr
:
template<
template<class...> class AssociativeContainer,
class... Keys
>
decltype(auto) Find(const AssociativeContainer<...>& rootContainer, Keys&&... keys)
{
...
}
Is there a way to use templates to generalize the logic? Something with parameter packs for keys. Something that will go through each nested container, check for object availability and return it or nullptr at the end:
需要一些工作(也许比我更专业的人可以使它更简单)但肯定是可能的。
举例...给定一个自定义类型特征(和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
用作 return 类型(来自 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,递归情况还需要尾部 return 类型;举个例子
-> decltype(Find(cnt.find(std::forward<K0>(key0))->second, std::forward<Ks>(keys)...))
更简单的解决方案(需要 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;
}
这也允许获取对任何中间容器的引用,因为它不需要传递所有键。