考虑到constness,templated-ness和genericness,C++如何解析专用模板?

Cla*_*diu 6 c++ templates overloading overload-resolution

我有以下代码,可能看似令人费解,但来自真正的代码:

#include <iostream>
using namespace std;

template <class Hrm, class A>
void foo(Hrm& h, A& a)
{
  cout << "generic" << endl;
}

template <template <bool> class Hrg>
void foo(Hrg<false>& h, int& a)
{
  cout << "specialized int" << endl;
}

template <template <bool> class Hrg>
void foo(Hrg<true>& h, const int& a)
{
  cout << "specialized const-int" << endl;
}

template <bool W>
struct what;

template<> struct what<true> { };
template<> struct what<false> { };


int main() {
  what<true> wt;
  what<false> wf; 

  int i = 5;
  const int& ri = i;

  foo(wt, i);  // 1) generic
  foo(wf, i);  // 2) specialized int
  foo(wt, ri); // 5) specialized const-int
  foo(wf, ri); // 6) generic
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

Ideone链接.

我明白了4:有没有专门针对虚假Hrgconst int,所以仿制药就会被调用.

我的问题是,为什么给定的函数调用其他情况?3似乎称为专门的const版本,因为const int匹配"更直接" A.我想知道为什么会更具体地发生这种情况.

而且,那12?特别是,1对我来说非常惊讶:为什么要generic调用版本而不是专门的const-int?


另外需要注意:如果我将两个foo专业更改为:

template <template <bool> class Hrg>
void _foo(Hrg<false>& h, int& a)
{
  cout << "specialized int" << endl;
}

template <template <bool> class Hrg>
void _foo(Hrg<true>& h, const int& a)
{
  cout << "specialized const-int" << endl;
}

template <class Hrg>
void foo(Hrg& h, int& a)
{
  return _foo(h, a);
}

template <class Hrg>
void foo(Hrg& h, const int& a)
{
  return _foo(h, a);
}
Run Code Online (Sandbox Code Playgroud)

然后输出变为:

foo(wt, i);     // a) specialized const-int
foo(wf, i);     // b) specialized int
foo(wt, ri);    // c) specialized const-int
//foo(wf, ri);  // d) compilation error
Run Code Online (Sandbox Code Playgroud)

这对我来说是一个更直观的结果.

Bri*_*ian 4

重载解析按以下步骤进行:

  1. 组装一组候选函数。这组候选函数由非模板函数和函数模板的特化组成。如果函数模板上的模板参数推导失败,则会将其从候选集中删除。
  2. 候选函数的子集被确定为可行。这意味着它具有的参数数量与参数数量兼容,并且每个参数都可以隐式转换为相应的参数类型。(请注意,即使转换不明确,函数也是可行的;但在这种情况下,仍可能选择该函数,然后您会收到编译错误)。
  3. 比较可行的功能。对于给定的一对可行函数,从某种意义上来说,为了从给定参数初始化函数参数而需要较少隐式转换的函数被认为比另一个函数“更好”。请注意,给定两个函数,可能没有一个比另一个更好。通常,这些规则足以证明单个可行的功能优于所有其他功能。该函数将赢得重载解析。
  4. 如果有两个函数并且没有一个比另一个更好,那么在某些情况下 [1] 会应用决胜规则,该规则仍然可能确定一个函数比另一个函数更好。如果规则3无法确定两个可行函数中哪一个更好,但只有一个是非模板,则非模板更好;如果两者都是模板专业化,但其中一个是从比另一个更专业的模板生成的,则该函数更好。在决胜局之后,如果有一个最佳可行函数(比所有其他函数都好),则该函数将赢得重载决策。如果不明确性仍然存在,则重载解析失败并且调用不明确。

请务必记住,第 4 步是在第 3 步之后进行的;“通用性”或“模板性”完全是一个打破平局的规则。

让我们回顾一下第一个代码块中的所有示例。

(1) 第一次、第三次超载扣除成功;Hrg无法推导出第二个。因此,候选人是第一和第三(规则 1)。两者都是可行的(规则 2)。第一个重载将绑定iint&,而第三个重载将绑定iconst int&。首选结合简历资格较少的参考文献(规则 3)。(巴里有标准中的具体引用。)第一个(通用)重载获胜。

(2)Hrg无法针对第三个超载推出,因此它不是候选者(规则 1)。第一个和第二个是候选者并且是可行的(规则2)。第一个和第二个重载都完全匹配,不需要转换,并且根据规则 3 无法区分。第二个重载获胜,因为它更专业(规则 4)。

(5) 第二次超载扣除Hrg失败,因此不是候选者,而第一和第三次超载则为候选者(规则1)。请注意,对于第一个重载,A被推导为const int,产生与第三个重载相同的签名。它们都是可行的(规则 2),并且在规则 3 结束时无法区分。第三个超载获胜,因为它更专业(规则 4)。

(6) 第三次超载扣除Hrg失败,因此不是候选者,而第一次和第二次是候选者(规则1)。第二个重载不可行(规则 2),因为int&无法绑定到ri,即const。第一个重载(通用重载)是唯一可行的函数,因此它获胜。

我将重载解析留在第二个代码块中作为读者的练习。

[1] 正如 TC 在评论中指出的那样,这里有一个微妙之处。仅当对于给定的函数对,从实参初始化参数所需的隐式转换序列对于每对对应的参数具有相同的排名时,才适用平局规则。如果第一个函数对于一个参数具有更好的隐式转换序列,而第二个函数对于另一个参数具有更好的隐式转换序列,则不应用平局规则,并且歧义仍然存在。不过,这种情况不会出现在问题的示例中。