函数特化/重载规则示例

Function specialisation / overloading rules example

我知道你不能部分特化一个函数模板,我也理解典型的函数重载。

我需要帮助的是摸索以下 4 个 foo() 函数之间的区别。我希望其中一些是完全相同的东西的不同语法?

是否有知识渊博的人可以解释每个函数究竟发生了什么,它是模板特化还是重载,以及 c++ 编译器如何确定调用什么?

//(1)
template<typename T>
void foo(T t)
{
    cout << "template<T> foo(T) " << endl;
}

//(2)
template<>
void foo<int*>(int* l)
{
    cout << "template<> foo<int*>(int*) " << endl;
}

//(3)
template<typename T>
void foo(T* l)
{
    cout << "template<T> foo(T*) " << endl;
}

//(4)
void foo(int* l)
{
    cout << "normal overload foo(int*) " << endl;
}

int main()
{
    int x = 0;
    foo(x);
    foo(&x);
    return 0;
}

程序输出:

template<T> foo(T)
normal overload foo(int*)

评论中的解释:

// the 1st primary function template, overloaded function template
template<typename T>
void foo(T t)

// full template specialization of the 1st function template with T = int*
template<>
void foo<int*>(int* l)

// the 2nd primary function template, overloaded function template
template<typename T>
void foo(T* l)

// non-template function, overloaded function
void foo(int* l)

int main()
{
    int x = 0;
    foo(x);  // only match the 1st function template; the others take pointer as parameter
    foo(&x); // call the non-template function, which is prior to templates in overload resolution
    return 0;
}

查看有关 overload resolution and explicit (full) template specialization 的更多详细信息。

让我们来看看第一个电话,foo(x)

int不能写成指针,无法推导参数,next

void foo(int* l);

无法将 int 隐式转换为 int*,接下来

template<typename T>
void foo(T t);

这似乎是一个很好的匹配,就像记住它作为 2)。下一个

template<>
void foo<int*>(int* l);

无法将 int 隐式转换为 int*,接下来

template<typename T>
void foo(T* l)

因此,唯一可能的匹配项是 2),因此 template<T> foo(T) 是输出。


第二次通话,foo(&x)

void foo(int* l);

完美匹配x类型的非模板函数。让我们记住这一点。

template<typename T>
void foo(T t);

好匹配!不过上一个还是比较好,下一个

template<>
void foo<int*>(int* l);

哦,之前模板的特化与类型完全匹配,这样更好,但是 1) 仍然是更好的匹配。下一个

template<typename T>
void foo(T* l)

比没有模板的专业化更好,但不比没有模板的模板好。

所以,最后调用了非模板函数。非模板总是优于模板。

foo(x) 的解析很容易发现,因为除了 template<typename T> void foo(T t) 之外没有其他候选函数。

所以我们的问题简化为:为什么 void foo(int* l) 调用 foo(&x)

  1. 调用模板方法template<> void foo<int*>(int* l) , 你需要调用 foo<int*>(&x)
  2. template<typename T> void foo(T* l) 通过调用 foo<int>(&x)
  3. template<typename T> void foo(T t) 通过调用 foo<int*>(&x)

如您所见,调用 foo(&x) 与上述三种显式模板调用方式都不匹配。

请注意,在没有专门函数 void foo(int* l) 的情况下,函数调用 foo<int*>(&x) 将由专门的 模板函数 template<> void foo<int*>(int* l) 解析击败非专业化的 模板函数 template<typename T> void foo(T t)

当试图确定重载时调用什么函数时,规则是:总是找到需要最少优化的函数来使用。

当用 int 调用 foo() 时,唯一可以使用的函数是第一个函数,因为您不能将 int 转换为 int*。

与 int* 一起使用时:
函数 4 无需任何优化即可使用。
函数 3 需要更改 T->int 以便 运行.
函数1需要将T改为指针类型,并确定T指向int。
函数 2 是函数 1 的特化

所以函数4可以立即使用,函数2需要更新模板类型,函数1,2需要更新模板类型和对象类型。
所以他们按顺序被调用:4 然后 3 然后 2 然后 1