问题描述
express
template<typename T>
void print(T t) {
std::cout << t << std::endl;
}
template<typename Key,typename Value,typename F>
void traverse(std::map<Key,Value>& m,F f) {
for (auto&& [key,value] : m) {
f(value);
}
}
int main()
{
std::map<int,std::string> M;
// initializing the map
traverse(M,print);
}
显式Could not deduce template argument for 'F'
解决了这个问题。
我的问题确实是为什么编译器无法推断出打印功能的模板类型?
据我了解,所有编译时间信息均可用。
解决方法
模板自变量推导基于函数的模板参数,函数的签名以及所提供的自变量的类型进行。而已。即使忽略print
是模板函数的事实(因此没有可用于推导的类型),traverse
的签名中也没有任何东西可以使编译器有任何想法推断的类型将是print<std::string>
的类型。任何此类信息都将在函数的定义中。
在traverse
实例化后尚不存在且不能存在的定义。而且,只有具有模板参数,您才能实例化模板。模板自变量推导旨在弄清楚哪种方法。
而且,如前所述,print
不是函数;它是 template 的名称。它没有类型。它甚至不是功能。它是一种基于模板参数生成函数的方法。即使您试图将print
传递给不是模板的函数,该代码仍然无法使用。编译器只能在调用时对函数执行模板参数推导,而不能在将它们作为函数参数传递时进行。在所有非推论情况下,您都必须直接提供模板参数。
并非所有必需的编译时间信息都可用,尤其是print
模板实例化参数也不可用。
traverse
是一个函数模板,它接受第二个参数的任何参数类型,但是名称print
本身并不表示任何特定类型,仅表示模板名称,并且编译器无法推断您打算将模板传递给模板的哪个具体实例。
显然,您希望编译器从print<std::string>
类型的模板参数推导print
的{{1}}实例。为此,您可以在M
模板中进行指定,使traverse
参数的类型取决于f
的类型,例如,删除不相关的m
,像这样:
typename F
,
编译器无法推论print
的类型,因为它忽略了traverse
的主体和所有不相关的参数。当您调用traverse(M,print)
时,编译器将尝试确定打印类型,就像您刚刚调用此example
一样:
template<typename F>
void example(F f);
example(print);
您可以通过以下方式更改traverse
的类型来编译代码:
template<typename Key,typename Value>
void traverse(std::map<Key,Value>& m,void(*f)(Value));
或通过指定特定的print
:
traverse(M,print<std::string>);