使用以下代码构建时
clang -Wall main.cpp -o main.o
生成以下诊断信息(在代码之后):
template <typename F>
void fun(const F& f)
{
}
template <typename F>
void fun(F f)
{
}
double Test(double d) { return d; }
int main(int argc, const char * argv[])
{
fun(Test);
return 0;
}
诊断:
main.cpp:17:5: error: call to 'fun' is ambiguous
fun(Test);
^~~
main.cpp:2:6: note: candidate function [with F = double (double)]
void fun(const F& f)
^
main.cpp:8:6: note: candidate function [with F = double (*)(double)]
void fun(F f)
^
1 error generated.
有趣的部分不是关于歧义错误本身(这不是这里的主要问题)。有趣的部分是模板参数
F
第一个 fun
解析为 double (double)
的纯函数类型, 而模板参数 F
第二个 fun
解析为更值得期待的double (*)(double)
函数指针类型,当仅使用函数名称调用 fun 时。但是,当我们更改
fun(Test)
的调用时成为 fun(&Test)
显式获取函数的地址(或显式函数指针),然后都是 fun
解析模板参数F
成为 double (*)(double)
!这种行为似乎是所有 Clang 和 GCC(以及 Visual Studio 2013)的共同行为。
那么问题来了:我的示例代码中给出的形式中的模板函数的函数类型模板参数推导规则是什么?
PS:如果我们添加另一个
fun
的实例取F* f
,那么似乎重载规则只是决定选择这个版本,根本没有报告任何歧义(尽管,正如我已经说过的,歧义不是之前最大的问题,但在最后一种情况下,我不知道为什么第三个版本是这里最好的匹配?)template <typename F>
void fun(F* f)
{
}
最佳答案
可能其他人可以比我更好地解释这一点,但这就是我的理解(没有引用标准,抱歉)。
不能复制函数类型的变量,因此在 template <typename F> void fun(F f)
中, F
不能具有函数类型。
但是,函数类型的变量可以转换为指向函数类型的指针(这称为“衰减”,就像数组到指针的转换一样),因此当匹配函数类型与 template <typename F> void fun(F f)
时, F
必须是指向一个函数。
在处理对函数类型的引用时,不会发生函数到指针的衰减(我在标准中找不到,但应与引用到数组的规则一起描述),因此在匹配模板 <typename F> void fun(const F& f)
时, F
是一个函数类型(并且参数的类型是对函数的引用)。
关于C++函数类型模板参数推导规则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22359879/