首页 > 解决方案 > 使用嵌套映射的可变模板参数推断

问题描述

在 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

有没有办法让这个设置正常工作?

标签: c++templatesvariadic-templatestype-inferencetemplate-argument-deduction

解决方案


玩太晚了?

在我看来,给定一个 recursive 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; 
 }

推荐阅读