以下代码:
#include<functional>
template<typename T>
void f(std::function<void(T)> g) {
}
template<typename T>
void g(T x) {
}
int main() {
f(&g<int>);
}
C ++ 14编译器产生错误:
no matching function for call to 'f(<unresolved overloaded function type>)'
f(&g<int>);
我很好奇为什么模板参数推断在这里不起作用。
看来,假设g的参数是int类型,我们可以推断f的参数类型为std::function<void(int)>
,因此f中的T = int
。为什么这不会发生?我对C ++标准的相关部分感兴趣,这解释了这一点。 T是否出现在非推断的上下文中?
以下类似的代码编译:
#include<vector>
template<typename T>
void f(std::vector<T> vec) {
}
int main() {
f(std::vector<int>{});
}
所以它不是创建非演绎语境的尖括号。
答案 0 :(得分:9)
你的函数需要一个std::function
类型的参数,但是你要传递一个指向函数的指针。 &g<int>
可转换为std::function
参数类型,但模板参数扣除需要完全匹配([temp.deduct.call]/2,3,4允许的调整除外),用户定义的转换不是考虑。
写f(std::function<void(int)>(g<int>))
会有效,因为您现在正在传递std::function
,因此模板参数推断会成功。
f<int>(&g<int>)
也有效,因为您现在已明确指定T
,它不再参与模板参数推断,并且将尝试用户定义的转换。
答案 1 :(得分:2)
&g<int>
的类型为void(*)(int)
。因此,编译器会尝试生成一个带有签名void f<>(void(*)(int))
的函数,而这个函数无法从模板中执行。类型std::function<void(int)>
是完全不同的类型。
在类似的代码中,对象std::vector<int>{}
的类型为std::vector<int>
,因此编译器会尝试生成一个函数void f<>(std::vector<int>)
,它可以通过推导{{1成为T
。
指定int
时,编译器无需推断类型,因此无法执行此操作。此外,虽然在推导的上下文中没有考虑隐式转换,但在非推导的上下文中它们 被认为是。因此,通过显式提供类型并因此使函数参数成为非推导的上下文,您允许编译器使用隐式转换来使用f<int>
初始化std::function<void(int)>
参数。