c++模板函数的重载解析

C++ template functions overload resolution

本文关键字:重载 函数 c++      更新时间:2023-10-16

我有以下代码:

#include <iostream>
template <typename T>
void f (T) { std::cout << "f(T)" << std::endl; }
template <typename T>
void f (bool) { std::cout << "f(bool)" << std::endl; }
int main ( )
{
    f(true);        // #1 prints f(T)
    f<bool>(true);  // #2 prints f(bool)
}

#1呼叫f(T), #2呼叫f(bool)

为什么会发生这种情况?选择重载模板函数的规则是什么?

我明白,在第一次调用编译器只是无法推断T,而试图调用第二个函数,所以选择第一个。

在第二个调用中,第二个函数在gcc上被认为是更好的匹配,而第一个函数在VS2013下被选择。谁做的是对的?顺便说一下,我还是对整个过程的详细描述很感兴趣。

非特化函数模板也称为底层基模板。基模板可以专门化。重载规则用于查看在不同情况下调用哪些重载规则,非常简单,至少在高层是这样:

  • 非模板函数是一等公民。与参数类型匹配的普通旧非模板函数以及任何函数模板都将被选择,而不是其他同样好的函数模板。

  • 如果没有一级公民可供选择,那么作为二级公民的函数基模板将被下一步参考。选择哪个函数基模板取决于哪个匹配最好,并且是"最专门化的"(重要注意:奇怪的是,"专门化"的使用与模板专门化无关;这只是一个不幸的口语)根据一组相当晦涩的规则:

    • 如果很明显有一个"最专门"的函数基模板,那么就使用这个模板。如果该基模板恰好是为所使用的类型专门化的,则将使用专门化,否则将使用用正确类型实例化的基模板。

    • Else(如您的情况)如果存在"最专门化"函数基模板的匹配,则调用是模糊的,因为编译器无法决定哪个是更好的匹配。程序员必须做一些事情来限定调用,并指出哪一个是需要的。

    • 如果没有可以匹配的函数基模板,则调用是错误的,程序员将不得不修复代码。

如果您想自定义函数基模板并希望该自定义参与重载解析(或者,总是在精确匹配的情况下使用),请使其成为一个普通的旧函数,而不是专门化。而且,如果您确实提供了重载,请避免同时提供专门化。

以上摘自 Herb Sutter的文章,在突出显示的项目符号中,您可以看到问题的根源

编辑

如果你在Visual Studio 2012中尝试(不要这么做)上面的代码,你会得到

致命错误LNK1179:无效或损坏的文件:重复的COMDAT '??美元f@_N@@YAX_N@Z '

,正如这里所解释的,这是因为

你做了一些无效的c++"诡计",它通过了编译器,但你现在有一个无效的*。Obj,它会阻塞链接器。

和下面这行都是错的

f(true);        // #1 prints f(T)

所以答案中的歧义不能保证解决

实际上您想要的是模板专门化,在您的情况下,应该这样写:

template<> // Without any typename in it!
void f (bool) { std::cout << "f(bool)" << std::endl; }