17

令我惊讶的是,这个程序可以在 MSCV 和 GCC 中编译:

class A
{
public:
  int add() { return 0; }
  template<typename T>
  T add() { return T(); }
};

int main() { 
  A a;
  a.add();
  a.add<int>();
  return 0; 
}

显然,由于模板化方法的类型无法推断,需要明确说明,所以情况并不模棱两可——仍然看起来有点阴暗——如果它是一个非模板化的方法,那显然不正确。

我试过谷歌搜索并查看标准的最后一个草案,但找不到答案 - 模板方法和普通方法的命名相同,只是在 C++ 中的返回类型合法不同,或者编译器只是允许的?

4

1 回答 1

17

这一直是合法的 C++。

14.5.6/2:

一个函数模板可以被其他函数模板和普通(非模板)函数重载。普通函数与函数模板无关(即,它从不被视为特化),即使它与可能生成的函数模板特化具有相同的名称和类型。

当使用类似的“template-id”语法add<int>时,只考虑具有足够模板参数的模板函数。所以a.add<int>()甚至不看非模板是否add匹配。

当标识符同时命名普通函数和函数模板时,编译器将尝试推断函数模板的模板参数以获得模板函数特化。然后通过通常的函数重载逻辑比较所有普通函数和所有模板函数特化。[见 13.3.1/7。]

在您的示例中,调用a.add()无法推断T模板版本的模板参数。所以唯一可行的功能是非模板重载。

在类似情况下还会出现另一条规则:如果非模板函数和模板函数特化否则会是模棱两可的重载,则非模板函数获胜。[这条规则在第 13.3.3 节中,在给定参数集的定义中,是什么使一个函数比另一个函数更好。]

class B
{
public:
  int f(int n) { return n+1; }

  template<typename T>
  T f(T n) { return n; }
};

int main() {
  B b;
  b.f(1);       // both are viable, non-template wins
  b.f<int>(1);  // only the template is viable
  return 0;
}

这是有道理的,因为模板仍然可以被其他专业化使用,或者显式使用<尖括号>。因此,使用非模板函数重载函数模板有点像添加显式特化,但麻烦更少。

于 2013-05-31T20:27:34.457 回答