嵌套映射的可变参数模板参数推断
Variadic template parameter inference with nested maps
使用 C++ 工作时,有时我会使用嵌套映射。例如,假设:
enum Color { RED, GREEN, BLUE};
enum Shape { CIRCLE, SQUARE, TRIANGLE };
std::unordered_map<Color, std::unordered_map<Shape, int>> shapeColorCount;
对于这些情况,使用可变参数模板编写 setter 和 getter 函数会很有用,这些函数是根据键的类型模板化的。目标类似于:
template<typename TValue, typename TKeys...>
TValue& nestedGet(MapTypeHere t_map, const TKeys&... t_keys);
void nestedSet(MapTypeHere t_map, const TValue& t_value, const TKeys&... t_keys);
递归定义这些函数并不太难,但我的主要问题是让模板参数的类型推断正常工作。问题是指定 MapTypeHere。我几乎可以写出类似
的东西
template<typename TValue, typename TKey>
using Map = std::unordered_map<TKey, TValue>;
template<typename TValue, typename TOuterKey, typename... TInnerKeys>
using Map = std::unordered_map<TOuterKey, Map<TValue, TInnerKeys...>;
template<typename TValue, typename... TKeys>
TValue& nestedGet(Map<TValue, TKeys...>& t_map, const TKeys&... t_keys);
void nestedSet(Map<TValue, TKeys...>& t_map, const TValue& t_value, const TKeys&... t_keys);
试图创建一个递归 using 指令,但它抱怨说我在尝试使用 Map
的基本情况时试图在非包模板变量中使用参数包。如果我将它们包装在结构中,它似乎允许它使用声明进行递归,但是我遇到了类型推断不起作用的问题。回到上面的例子:
std::unordered_map<Color, std::unordered_map<Shape, int>> shapeColorCount
nestedSet<int, Color, Shape>(shapeColorCount, 5, Color::RED, Shape::SQUARE); // This works
nestedSet(shapeColorCount, 5, Color::RED, Shape::SQUARE); // It can't figure out the types for the template
有没有办法让这个设置正常工作?
是的,可以编写如下函数:
template<typename Map, typename Value, typename FirstKey, typename ...RestKeys>
void nestedSet(Map& map, Value const& value, FirstKey const& key, RestKeys const&... rest_keys)
{
if constexpr(sizeof...(RestKeys) == 0)
map[key] = value;
else
nestedSet(map[key], value, rest_keys...);
}
template<typename Map, typename FirstKey, typename ...RestKeys>
auto& nestedGet(Map& map, FirstKey const& key, RestKeys const&... rest_keys)
{
if constexpr(sizeof...(RestKeys) == 0)
return map[key];
else
return nestedGet(map[key], rest_keys...);
}
请注意,此解决方案不依赖于特定的 unordered_map<Color, std::unordered_map<Shape, int>>
实例化。它适用于键和值类型的任何实例化,以及嵌套的任何深度 unordered_map
s.
这是 demo。
此外,如果您没有 c++17,则可以使用采用单个 KeyType
参数的重载模板重写 if constexpr
解决方案。
来不及玩了?
在我看来,给定一个递归 nestedGet()
,您可以使用 if constexpr
(如果您可以使用 C++17)或如下使用重载来编写,
template <typename M, typename K>
auto & nestedGet (M & map, K const & key)
{ return map[key]; }
template <typename M, typename K, typename ... RKs>
auto & nestedGet (M & map, K const & key, RKs const & ... rks)
{ return nestedGet(map[key], rks...); }
nestedSet()
函数可以写在nestedGet()
上,如下所示
template <typename M, typename V, typename ... Ks>
void nestedSet (M & map, V const & value, Ks const & ... keys)
{ nestedGet(map, keys...) = value; }
下面是一个完整的编译示例
#include <iostream>
#include <unordered_map>
enum Color { RED, GREEN, BLUE};
enum Shape { CIRCLE, SQUARE, TRIANGLE };
template <typename M, typename K>
auto & nestedGet (M & map, K const & key)
{ return map[key]; }
template <typename M, typename K, typename ... RKs>
auto & nestedGet (M & map, K const & key, RKs const & ... rks)
{ return nestedGet(map[key], rks...); }
template <typename M, typename V, typename ... Ks>
void nestedSet (M & map, V const & value, Ks const & ... keys)
{ nestedGet(map, keys...) = value; }
int main ()
{
std::unordered_map<Color, std::unordered_map<Shape, int>> shapeColorCount;
nestedSet(shapeColorCount, 42, Color::RED, Shape::SQUARE);
std::cout << nestedGet(shapeColorCount, Color::RED, Shape::SQUARE) << std::endl;
}
使用 C++ 工作时,有时我会使用嵌套映射。例如,假设:
enum Color { RED, GREEN, BLUE};
enum Shape { CIRCLE, SQUARE, TRIANGLE };
std::unordered_map<Color, std::unordered_map<Shape, int>> shapeColorCount;
对于这些情况,使用可变参数模板编写 setter 和 getter 函数会很有用,这些函数是根据键的类型模板化的。目标类似于:
template<typename TValue, typename TKeys...>
TValue& nestedGet(MapTypeHere t_map, const TKeys&... t_keys);
void nestedSet(MapTypeHere t_map, const TValue& t_value, const TKeys&... t_keys);
递归定义这些函数并不太难,但我的主要问题是让模板参数的类型推断正常工作。问题是指定 MapTypeHere。我几乎可以写出类似
的东西template<typename TValue, typename TKey>
using Map = std::unordered_map<TKey, TValue>;
template<typename TValue, typename TOuterKey, typename... TInnerKeys>
using Map = std::unordered_map<TOuterKey, Map<TValue, TInnerKeys...>;
template<typename TValue, typename... TKeys>
TValue& nestedGet(Map<TValue, TKeys...>& t_map, const TKeys&... t_keys);
void nestedSet(Map<TValue, TKeys...>& t_map, const TValue& t_value, const TKeys&... t_keys);
试图创建一个递归 using 指令,但它抱怨说我在尝试使用 Map
的基本情况时试图在非包模板变量中使用参数包。如果我将它们包装在结构中,它似乎允许它使用声明进行递归,但是我遇到了类型推断不起作用的问题。回到上面的例子:
std::unordered_map<Color, std::unordered_map<Shape, int>> shapeColorCount
nestedSet<int, Color, Shape>(shapeColorCount, 5, Color::RED, Shape::SQUARE); // This works
nestedSet(shapeColorCount, 5, Color::RED, Shape::SQUARE); // It can't figure out the types for the template
有没有办法让这个设置正常工作?
是的,可以编写如下函数:
template<typename Map, typename Value, typename FirstKey, typename ...RestKeys>
void nestedSet(Map& map, Value const& value, FirstKey const& key, RestKeys const&... rest_keys)
{
if constexpr(sizeof...(RestKeys) == 0)
map[key] = value;
else
nestedSet(map[key], value, rest_keys...);
}
template<typename Map, typename FirstKey, typename ...RestKeys>
auto& nestedGet(Map& map, FirstKey const& key, RestKeys const&... rest_keys)
{
if constexpr(sizeof...(RestKeys) == 0)
return map[key];
else
return nestedGet(map[key], rest_keys...);
}
请注意,此解决方案不依赖于特定的 unordered_map<Color, std::unordered_map<Shape, int>>
实例化。它适用于键和值类型的任何实例化,以及嵌套的任何深度 unordered_map
s.
这是 demo。
此外,如果您没有 c++17,则可以使用采用单个 KeyType
参数的重载模板重写 if constexpr
解决方案。
来不及玩了?
在我看来,给定一个递归 nestedGet()
,您可以使用 if constexpr
(如果您可以使用 C++17)或如下使用重载来编写,
template <typename M, typename K>
auto & nestedGet (M & map, K const & key)
{ return map[key]; }
template <typename M, typename K, typename ... RKs>
auto & nestedGet (M & map, K const & key, RKs const & ... rks)
{ return nestedGet(map[key], rks...); }
nestedSet()
函数可以写在nestedGet()
上,如下所示
template <typename M, typename V, typename ... Ks>
void nestedSet (M & map, V const & value, Ks const & ... keys)
{ nestedGet(map, keys...) = value; }
下面是一个完整的编译示例
#include <iostream>
#include <unordered_map>
enum Color { RED, GREEN, BLUE};
enum Shape { CIRCLE, SQUARE, TRIANGLE };
template <typename M, typename K>
auto & nestedGet (M & map, K const & key)
{ return map[key]; }
template <typename M, typename K, typename ... RKs>
auto & nestedGet (M & map, K const & key, RKs const & ... rks)
{ return nestedGet(map[key], rks...); }
template <typename M, typename V, typename ... Ks>
void nestedSet (M & map, V const & value, Ks const & ... keys)
{ nestedGet(map, keys...) = value; }
int main ()
{
std::unordered_map<Color, std::unordered_map<Shape, int>> shapeColorCount;
nestedSet(shapeColorCount, 42, Color::RED, Shape::SQUARE);
std::cout << nestedGet(shapeColorCount, Color::RED, Shape::SQUARE) << std::endl;
}