C++ 模板函数重载决议

Posted

技术标签:

【中文标题】C++ 模板函数重载决议【英文标题】:C++ template functions overload resolution 【发布时间】:2014-04-20 03:02:46 【问题描述】:

我有以下代码:

#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 下选择的。谁在这里做正确的事?顺便说一句,我仍然对过程的完整描述感兴趣。

【问题讨论】:

不知道规则,但我看不出编译器如何在你的第二个模板中推断出T,所以我看不出如果没有明确的参数如何选择它。 (但同样,我对模板细节一无所知。) 【参考方案1】:

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

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

如果没有至少一样好的一等公民可供选择,那么接下来咨询二等公民的功能基本模板。选择哪个函数基模板取决于哪个匹配最好并且是“最专业的”(重要说明:奇怪的是,“专业”的这种使用与模板专业化无关;这只是一种不幸的口语)根据一组公平神秘的规则:

如果很明显有一个“最专业”的函数基础模板,那么就会使用那个模板。如果该基本模板恰好专门用于正在使用的类型,则将使用该专门化,否则将使用使用正确类型实例化的基本模板。

否则(如您的情况)如果“最专业”的函数基本模板存在相同的情况,则调用不明确,因为编译器无法确定哪个更匹配. 程序员必须做一些事情来限定调用并说出需要哪个调用。

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

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

以上内容摘自 the Herb Sutter 的 this 帖子,在突出显示的项目符号中,您可以看到问题的根源

编辑

如果您在 Visual Studio 2012 中尝试(不要这样做)上述代码,您会得到 ​​p>

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

正如here 解释的那样,这是因为

你做了一些无效 C++ 的“诡计”,它通过了编译器,但你现在有一个无效的 *.obj,它阻塞了链接器。

下面这行是罪魁祸首

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

所以答案中解释的歧义没有保证解决

【讨论】:

您可能想感谢 Herb Sutter 的摘录,参见。 gotw.ca/publications/mill17.htm. 那么,第二个调用是歧义?第一个电话是怎么回事? Herb 的解释我已经看了好几遍了,还是不明白。我认为这取决于“最专业”的含义。有人会认为 f(bool) 更专业,但显然不是,无论在什么意义上,这里使用了“专业”。我不明白的第二件事是 f(true) 和 f(true) 之间存在区别,因为 T 在第一种情况下也必须是布尔值。 基本模板都是模板,它们都不是专门化的(注意重要提示:奇怪的是,这种“专门化”的使用与模板专门化无关)胜任、有资格、能干的话会更清楚。首先选择基本模板,然后我们继续进行规范化。在这种情况下,基本模板的选择是不明确的 @lisyarus 很抱歉,但我仍然不明白为什么调用 "f(true); // #1 prints f(T)" 是模棱两可的?它应该是带有 "f(true); // #2 打印 f(bool)" 的那个吗?由于在 #1 中无法为第二个函数模板推导出 T,因此第一个函数模板是完美匹配的。但是对于#2,就很模糊了。我错过了什么?【参考方案2】:

实际上你想要的是一个模板专业化,在你的情况下,应该这样写:

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

这在 VS2012 中按预期工作。

【讨论】:

以上是关于C++ 模板函数重载决议的主要内容,如果未能解决你的问题,请参考以下文章

函数重载与函数模板 - C++

C++中函数模板和模板函数的区别

实验2:函数重载函数模板简单类的定义和实现

虚函数和虚拟继承的内存分布

C++ Primer 5th笔记(chap 16 模板和泛型编程)重载与模板

具有函数模板和重载的 C++ 类