繁体   English   中英

包装递归可变参数模板类会更改行为。 为什么?

[英]Wrapping a recursive variadic template class changes behavior. Why?

希望这会激起社区中的一些人的兴趣。 希望它不是太明显,因为我不确定发生了什么。 我创建了带有递归定义的可变参数模板类,主要是作为一个有趣的自我挑战。 有点像元组一样,这个类创建unordered_maps的unordered_maps,任意深度,每层都有任意键类型。 例如,您可以创建nested_map<int, std::string, float, int> ,然后使用map["fred"][3.4][42] = 35;设置它map["fred"][3.4][42] = 35; 这是代码 - 不是太疯狂。

template<typename T, typename K, typename ... KS> struct nested_map_base : std::unordered_map<K, T>
{
  T &operator[](const K &key)
  {
    // just to verify we get to the bottom of things recursively
    std::cout << "base: key = " << key << std::endl;

    return this->std::unordered_map<K, T>::operator[](key);
  }
};

template<typename T, typename New_K, typename K, typename ... KS>
struct nested_map_base<T, New_K, K, KS ...>
: std::unordered_map<New_K, nested_map_base<T, K, KS...>>
{
  nested_map_base<T, K, KS...> &operator[](const New_K &new_key)
  {
    // just for debugging and to demonstrate that it's working
    // for purposes of this question
    std::cout << "midway: key = " << new_key << std::endl;

    return this->std::unordered_map<New_K, nested_map_base<T, K, KS...>>::operator[](new_key);
  }
};

工作正常。 运行以下代码,得到预期的输出 -

std::cout << "Method1:" << std::endl << std::endl;
nested_map_base<int, std::string, double, int> test_nest;
std::cout << "insert" << std::endl;
test_nest["leonard"][4.8][45] = 111;
std::cout << "retrieve" << std::endl;
int &answer = test_nest["leonard"][4.8][45];
std::cout << "Aanswer should be 111. Answer is " << answer << std::endl << std::endl;

生产 -

Method1:

insert
midway: key = leonard
midway: key = 4.8
base: key = 45
retrieve
midway: key = leonard
midway: key = 4.8
base: key = 45
Aanswer should be 111. Answer is 111

整齐。 然后我想我想将它包装在外部类中以保持实现私有,所以我就这样开始 -

template<typename datum_type, typename ... keys> class nested_map
{
private:
  nested_map_base<datum_type, keys ...> backing_store;

public:
  template<typename Base_key, typename ... KS> auto operator[](const Base_key &key)
  {
    return backing_store[key];
  }
};

没有什么,并且起初它似乎工作,但以下代码产生不同的结果 -

std::cout << "Method2:" << std::endl << std::endl;
nested_map<int, std::string, double, int> test_nest;
std::cout << "insert" << std::endl;
test_nest["leonard"][4.8][45] = 111;
std::cout << "retrieve" << std::endl;
int &answer = test_nest["leonard"][4.8][45];
std::cout << "Answer should be 111. Answer is " << answer << std::endl << std::endl;

它产生了这个 -

Method2:

insert
midway: key = leonard
midway: key = 4.8
base: key = 45
retrieve
midway: key = leonard
midway: key = 4.8
base: key = 45
Answer should be 111. Answer is 0

递归可变参数模板元编程充满了陷阱,并且有理由不会经常包装,所以我并不感到震惊的是包裹的那个不起作用,但让我感到惊讶的是它怎么没有工作。 它按预期递归,直到包含终端基准类型的std::unordered_map 在调试器中,从终端映射中恢复了对int的引用,并且在简单测试代码中将其设置为111。 您第二次看到键被递归的事实表明检索过程似乎也在起作用,但引用的是零值int。 好奇。

我正在深入挖掘调试器,以查看,例如,集合引用的实际地址值是否与用于检索的引用相同。 我认为它们可能不同的唯一方法是,例如,倒数第二个递归层返回最后一层的临时值,而不是引用数据结构中的那个。 或者也许在包装的情况下,它们都是临时的而不是参考......类似的东西,但包装是如此轻,似乎不可能。 所以如果我发现更多内容,我会添加评论,但我想我会把它扔给社区,看看是否有一些不同的眼睛可以通过检查来挑逗。

在“ 模板参数演绎”的“Cppreference”页面中有一个关于自动返回功能的部分,该部分描述了当auto用作函数返回时的规则。

模板参数推导是在功能,演绎的含义是,当使用申报auto在函数的说明符return类型,从return的语句。

对于自动返回函数,参数P如下获得:在T ,包含auto的函数的声明的返回类型,每次出现的auto都被虚构的模板参数U替换。 参数A是return语句的表达式,如果return语句没有操作数,则Avoid() 按照上述规则从PA扣除U后,将推导出的U代入T以得到实际的返回类型。

这可以解释为什么auto&工程和auto没有。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM