【问题标题】:Partial ordering of function templates containing template parameter packs包含模板参数包的函数模板的部分排序
【发布时间】:2016-01-30 07:00:02
【问题描述】:

包含模板参数包的函数模板的部分排序独立于这些模板参数包的推导参数数量。

template<class...> struct Tuple { };
template<          class... Types> void g(Tuple<Types ...>);        // #1
template<class T1, class... Types> void g(Tuple<T1, Types ...>);    // #2
template<class T1, class... Types> void g(Tuple<T1, Types& ...>);   // #3

g(Tuple<>());                     // calls #1
g(Tuple<int, float>());           // calls #2
g(Tuple<int, float&>());          // calls #3
g(Tuple<int>());                  // calls #3

以上引自partial ordering of overload function templates。我不太明白为什么g(Tuple&lt;int&gt;()); // calls #3。具体来说,为什么不能调用#2?以下是我的推理,如有错误请指出:

注意:我会忽略#1 b/c 解释得很好here

第 1 步:推演、代换和重载解决方案提出了这些:

  • void(Tuple) [T1 = int, Types 为空] #2
  • void(Tuple) [T1 = int, Types& 为空] #3

第 2 步:转换两个函数模板:

  • void g(Tuple);
  • void g(Tuple);

Step3:这是一个函数调用上下文,类型是函数调用有参数的那些函数参数类型:

  • 从元组 推导出元组 OK [T1 = C2;类型... = Pack&...]
  • 从元组 推导出元组) 好吗? [T1 = C1;类型和...呢?可以从Pack1推导出来吗...?此处是否删除了参考?]

3) 如果P是引用类型,那么P所引用的类型用于推导。This好像没问题。

如果 P 具有包含模板参数列表 的形式之一,则该模板参数列表的每个元素 Pi 与其 A 的相应模板参数 Ai 匹配。如果最后一个 Pi 是一个包扩展,然后将其模式与 A 的模板参数列表中的每个剩余参数进行比较。没有以其他方式推导的尾随参数包被推导为一个空参数包。

Step4:如果最后一步正确。那么这意味着#3 并不比#2 更专业。所以对于应该解析到哪个函数模板是模棱两可的。

更新:我想我误解了上面的相关引述。当我们将 P 中的模板参数与 A 中的模板参数匹配时,它们会逐字匹配,这意味着当我们匹配时,对 函数调用 参数和参数所做的所有转换和分析都不会再次应用P/A 中的模板参数/参数(实际上是函数调用参数/参数)。然后它会在上面的步骤 3 中无法从Tuple&lt; C1, Pack1...&gt;) 推导出Tuple&lt; T1, Types&amp;...&gt;。所以#3 更专业。

【问题讨论】:

  • 您是否尝试过各种编译器的最新版本?
  • 我只尝试过 g++。在哪里可以在线试用其他编译器?

标签: c++ templates overload-resolution


【解决方案1】:

解决重载问题的第一步是找到所有候选者。对于电话g(Tuple&lt;int&gt;()),有三个可行的候选人:

g(Tuple<int>); // #1: [Types = {int}]
g(Tuple<int>); // #2: [T1 = int, Types = {}]
g(Tuple<int>); // #3: [T1 = int, Types = {}]

从转换序列的角度来看,所有三个都是同样可行的候选者(当然,因为它们都采用相同的参数,即输入的精确匹配)。它们都是函数模板的特化,因此我们也无法在此基础上进行区分。

所以我们只剩下函数模板部分排序了。我们为每个重载合成类型,并尝试使用我们的合成类型针对其他每个重载执行模板推导。更简单的比较是 (1) vs (2) 和 (1) vs (3)。在那里,我们有 [temp.deduct.partial]:

如果 A 是从函数参数包转换而来的,而 P 不是参数包,则类型推导失败。

由于我们将Types...转化为UniqA...,第一个参数是T1,所以类型推导失败。这使得 (2) 和 (3) 都比 (1) 更专业。所以现在我们比较(2)和(3)。

首先,我们可以从 (3) 中推导出 (2) 吗?

template <class T1, class... Types> void g(Tuple<T1, Types...> );

g(Tuple<Uniq3, Uniq3Pack&...>());

当然,T1 == Uniq3Types... == Uniq3Pack&amp;... 没问题。接下来,我们尝试另一个方向:

template <class T1, class... Types> void g(Tuple<T1, Types&...> );

g(Tuple<Uniq2, Uniq2Pack...>());

这失败了,因为Uniq2Pack 不是一组引用类型,而Types&amp;... 是。由于演绎只在一个方向上成功,这使得 (3) 更专业化的重载。

由于它是最后一个,(3) 是最可行的候选者。这似乎违反直觉,因为我们实际上并没有使用引用类型来调用它 - 但它仍然是最佳选择。

【讨论】:

猜你喜欢
  • 2017-04-24
  • 2021-08-02
  • 1970-01-01
  • 1970-01-01
  • 2015-11-29
  • 2017-05-19
  • 2011-06-10
  • 1970-01-01
  • 2015-02-18
相关资源
最近更新 更多