C ++模板扣除不起作用

时间:2016-12-22 23:42:11

标签: c++ templates c++14 template-deduction

以下代码:

#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>{});
}

所以它不是创建非演绎语境的尖括号。

2 个答案:

答案 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)>参数。