查找调用哪个模板函数重载的规则

时间:2015-04-03 13:06:57

标签: c++ templates

//(1)
template <class T>
void f(T) {}

//(2)
template <class T>
void f(T*) {}

//(3)
template <>
void f<>(int*) {}

//(4)
void f(int*) {}

int main()
{
  int* p;
  f(p); // which function will be called?
  return 0
}

我已经知道了这个行为:

  • 这取决于是否存在过载(4)。
  • 取决于(1),(2)和(3)
  • 的声明顺序

令我烦恼的是,顺序很重要(因为它增加了我在编写代码时的不确定性和猜测工作),尤其是我知道如果那些是类特化,那么顺序无关紧要,最专业的类可以被编译器调用。

有人可以向我解释这是如何运作的吗?以及编译器在查找适当的函数特化时遵循的规则是什么?

2 个答案:

答案 0 :(得分:5)

基本规则实际上相当简单:函数和函数模板参与重载决策。显式函数模板特化不。

因此,对于调用f(p),编译器执行重载决策以在(1),(2)和(4)之间进行选择。 (3)从未考虑过载解析。

重载分辨率选择单个函数或函数模板。 完成后,如果它选择了一个功能模板,那么然后将考虑该模板的特化。

让我们分析您提供的声明的不同组合。

  • 如果存在(4),将明确选择。当其他条件相同时,非模板函数是比函数模板更好的重载匹配。

  • 假设我们按此顺序只有(1),(2)和(3)。过载分辨率选择在(1)和(2)之间。 (2)更好,因为它更专业。因此,通过重载决策选择函数模板(2)。

    然后,编译器查看模板是否具有任何特化。事实上,它确实是T = int的专业化(3)。所以选择了这种专业化。

  • 现在,让我们说顺序是(1),(3),(2)。同样,重载决策在(1)和(2)之间选择(记住特化,其中(3)是,从不参与重载决策)。 (2)再次被选中。这一次,(3)是(1)的特化,对于T = int*。这是因为(2)在声明(3)时尚不存在,所以没有(2)专门化。但是(1)没有通过重载决策选择,因此不予考虑。

  • 顺序(2),(3),(1)与(1),(2),(3)相同。顺序(2),(1),(3)与(1),(3),(2)相同。 (1)和(2)的相对顺序无关紧要 - 它是(3)的位置,它控制着它将专门化的模板。

答案 1 :(得分:2)

  

这取决于是否存在过载(4)。

...这是由于重载决策中的特殊规则:某些模板不具有特殊性的函数优先于函数模板特化,如果超重分辨率规则无法区分这些函数。 E.g。

template <typename T>
void f(T); // A

void f(int); // B

f(4); // Calls B - Conversions of 4 to both parameter types are equivalent

这也适用于您的样本:由于f<int>(1),f<int*>(2)和f(4)的参数类型相同,因此必须选择(4) 。

一旦(4)消失,部分排序开始:它确定(2)更专业,因为

  • 某些任意类型U无法与T*匹配(U不是指针),而

  • V*可以匹配一些任意指针类型T(因为T将简单地推断为V*)。

因此(2)中的专业化f<int>是更好的匹配。

  

取决于(1)(2)和(3)的声明顺序

您声明了名称f的两个函数模板重载。但是你声明的显式专业化可以兼顾两者!推导出模板参数,可以推导出f(T)f(T*),因为它们都可以指向int

template <class T> void f(T) {} // (1)

// Specializes f<int*> from (1), (2) not yet declared
template <> void f<>(int*) {}

template <class T> void f(T*) {} // (2)

或者

template <class T> void f(T)  {} // (1)
template <class T> void f(T*) {} // (2)

// Specializes f<int> from (2)
template <> void f<>(int*) {}

在后一种情况下,选择(2)因为它比(1)更专业(根据部分排序规则)。您可以通过显式提供模板参数来明确专业化,而不是将它们保留为未指定:

template <>
void f<int>(int*) {} // Can only specialize (2)