您能帮助我理解为什么参数推导对类模板有效而对函数模板无效吗?
如果我理解正确,则类模板定义了一个函数,因此当我调用时,编译器可以进行隐式强制转换,但是在使用函数模板的情况下,目前没有函数定义,因此是隐式的演员没有发生。
但是我不明白为什么编译器不能创建函数定义然后应用隐式强制转换?
#include <functional>
template<typename ...ARGS>
class Test1
{
public:
void add(const std::function<void(ARGS...)>&) {}
};
class Test2
{
public:
template<typename ...ARGS>
void add(const std::function<void(ARGS...)>&) {}
};
void func(int) {}
int main()
{
Test1<int> test1;
test1.add(func);
Test2 test2;
test2.add<int>(func);
}
错误是:
在函数'int main()'中:
25:24:错误:没有匹配的函数可以调用'Test2 :: add(void(&)(int))'
25:24:注意:候选人是:
14:10:注意:模板无效Test2 :: add(const std :: function&)
14:10:注意:模板参数推导/替换失败:
25:24:注意:类型'const std :: function'和'void(int)'不匹配
答案 0 :(得分:8)
在第一种情况下,您将显式实例化类模板Test1
。这意味着其add
成员的函数声明是使用签名add(const std::function<void(int)>&)
生成的。当编译器随后尝试解析test1.add(func)
时,只有一个候选。由于std::function<void(int)>
可以从void(int)
函数隐式构造,因此签名匹配,因此编译器只实例化成员函数定义,一切都很好。
在第二种情况下,编译器必须执行模板参数推导/替换,以查看其是否可以“使用” add
模板。您可能会认为,指定int
会确定模板参数,因此无需推论,但事实并非如此:可能是您打算部分指定模板参数,例如参见here 。换句话说,您可能试图使用比显式指定的参数更多的参数实例化函数模板,至少编译器不知道是否这样做。因此,它仍然必须尝试匹配std::function<void(ARGS...)>
(或更确切地说,std::function<void(int, ...)>
和void(int)
的类型,但是不能匹配,因为不考虑隐式转换。 / p>
简而言之:指定显式模板参数不会阻止可变参数函数模板的模板参数推导。
注意:我对100%的确切术语不是很坚定,感谢任何能够纠正我的语言律师!
编辑:我主要基于我读过的here。
答案 1 :(得分:2)
我最初对以下代码段为何起作用的理由是错误的,但是由于@NathanOliver帮助了我(请参阅下文),因此是经过修订的说明:在模板参数推导过程中,不执行任何类型转换。将函数指针传递给带有std::function
参数的函数需要进行这种转换。为了避免这个问题,您可以调用这样的方法
test2.add(std::function<void(int)>(func));
或将Test2
的定义调整为
class Test2
{
template<typename ...ARGS>
void add(void(*)(ARGS...)) {}
}
与原始通话一起使用
test2.add<int>(func);
在两个示例中,都不需要转换。之所以调用Test1::add
是因为在调用方法之前已经执行了模板类型推断,因此可以进行转换。
还要注意,当使用单个模板参数声明Test2
时,也会出现相同的问题
class Test2
{
template<typename T>
void add(const std::function<void(T)>&) {}
}
在呼叫者方面具有以下用例:
test2.add<int>(func); // Conversion ok, function template specified
test2.add(std::function<void(int)>(func)); // Type deduction, no conversion
test2.add(func); // Error, conversion AND type deduction