C++ 函数模板参数推导(类与函数模板)

C++ 函数模板参数推导(类与函数模板),c++,templates,std-function,template-argument-deduction,C++,Templates,Std Function,Template Argument Deduction,你能帮我理解为什么参数推导对类模板有效而对函数模板无效吗 如果我理解正确,类模板定义了一个函数,因此当我调用时,编译器可以进行隐式转换,但对于函数模板,目前没有函数定义,因此隐式转换不会发生 但我不明白为什么编译器不能创建函数定义,然后应用隐式强制转换 #包括 模板 类Test1 { 公众: void add(const std::function&){ }; 类Test2 { 公众: 模板 void add(const std::function&){ }; void func(int){}

你能帮我理解为什么参数推导对类模板有效而对函数模板无效吗

如果我理解正确,类模板定义了一个函数,因此当我调用时,编译器可以进行隐式转换,但对于函数模板,目前没有函数定义,因此隐式转换不会发生

但我不明白为什么编译器不能创建函数定义,然后应用隐式强制转换

#包括
模板
类Test1
{
公众:
void add(const std::function&){
};
类Test2
{
公众:
模板
void add(const std::function&){
};
void func(int){}
int main()
{
Test1 Test1;
test1.add(func);
Test2 Test2;
test2.add(func);
}
错误是:

在函数“int main()”中:

25:24:错误:调用“Test2::add(void(&)(int))”时没有匹配的函数

25:24:注:候选人为:

14:10:注意:模板void Test2::add(const std::function&)

14:10:注意:模板参数扣除/替换失败:

25:24:注意:类型“const std::function”和“void(int)”不匹配


在第一种情况下,您显式地实例化了类模板
Test1
。这意味着其
add
成员的函数声明是使用签名
add(const std::function&)
生成的。当编译器随后尝试解析
test1.add(func)
时,只有一个候选项。由于
std::function
可以从
void(int)
函数隐式构造,签名匹配,编译器只需实例化成员函数定义,一切正常

在第二种情况下,编译器必须执行模板参数推导/替换,以查看是否可以“使用”
add
模板。您可能认为,指定
int
将确定模板参数,因此无需进行推断,但事实并非如此:可能是您的意思是部分指定模板参数,请参见示例。换句话说,您可能试图用比显式指定的参数更多的参数实例化函数模板,至少编译器不知道您是否这样做。因此,它仍然必须尝试匹配
std::function
(或者更准确地说,
std::function
void(int)
)的类型,这是它无法做到的,因为推导中不考虑隐式转换

简而言之:指定显式模板参数不会阻止可变函数模板的模板参数推断

注:我不是100%的确切的术语坚定,任何语言律师纠正我是感激的


编辑:我主要是基于我所读到的内容。

我对以下代码片段的工作原理是错误的,但是@NathanOliver帮助了我(见下文),这里是修改后的解释:在模板参数推导过程中,不执行类型转换。将函数指针传递给接受
std::function
参数的函数需要这样的转换。要避免此问题,可以像这样调用该方法

test2.add(std::function<void(int)>(func));
它与原始调用一起工作

test2.add<int>(func);
调用方有以下用例:

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
test2.add(func);//转换正常,指定了函数模板
test2.add(std::function(func));//类型推断,无转换
test2.add(func);//错误、转换和类型推断

不幸的是,您的理由不正确,至少部分不正确。问题是函数指针不是
std::function
。因为它不是。编译器不会尝试将其转换为一个指针来确定模板类型。基本规则:在模板参数推断中不执行任何转换。@acocagua我认为您不是e正确。例如说“一个成员函数在被调用时被实例化”。编译器完全定义了类,但这仅仅是为了拼写成员函数签名。嗯,“确切的术语”问题…我们可以说签名已经被实例化了吗?问题是:随着类被实例化,成员函数是固定的(这就是我试图强调的…)。将不再有任何模板推导,因此转换是合法的。为了理解,在实例化类时是否已经生成代码,或者仅在第一次调用函数时才生成代码并不重要。我的意思是,尽管技术上正确,但我对术语“实例化”感到有些不舒服,与第二种情况一样,我们也实例化了函数(这次包括签名),这可能是误解的根源……我实际上试图在回答中涵盖这一确切区别:“这意味着生成了其
add
成员的函数声明”,并且“编译器只是实例化成员函数模板定义"。我不知道在引用模板实例化时,declare/define是否是正确的术语,但我试图尽可能清楚地说明这一点。尽管您可能是对的,实际代码生成与函数解析关系不大。您第一段的最后一句话没有意义:您没有成员函数模板,它只是一个简单的成员函数。尽管您确实不想使用语言律师术语,但您可以使用术语,因为它与“普通”术语有点不同
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