函数模板参数推导(class vs 函数模板)
Function template argument deduction (class vs funtion template)
您能帮我理解为什么参数推导适用于 class 模板而不适用于函数模板吗?
如果我没理解错,class 模板定义了一个函数,所以当我调用时,编译器可能会进行隐式转换,但是对于函数模板,没有函数定义目前,隐式转换没有发生。
但我不明白为什么编译器不能创建函数定义然后应用隐式转换?
#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);
}
错误是:
In function 'int main()':
25:24: error: no matching function for call to 'Test2::add(void (&)(int))'
25:24: note: candidate is:
14:10: note: template void Test2::add(const std::function&)
14:10: note: template argument deduction/substitution failed:
25:24: note: mismatched types 'const std::function' and 'void(int)'
在第一种情况下,您显式实例化了 class 模板 Test1
。这意味着其 add
成员的函数声明是使用签名 add(const std::function<void(int)>&)
生成的。当编译器随后尝试解析 test1.add(func)
时,只有那个候选者。由于 std::function<void(int)>
可以从 void(int)
函数隐式构造,签名匹配,编译器只是实例化成员函数定义,一切都很好。
在第二种情况下,编译器必须执行模板参数 deduction/substitution 以查看它是否可以 "use" add
模板。您可能认为指定 int
会确定模板参数,因此不需要进行推导,但事实并非如此:您的意思可能是部分指定模板参数,例如 here .换句话说,您可能试图用比您明确指定的更多的参数来实例化函数模板,至少编译器不知道您是否这样做。所以它仍然必须尝试匹配 std::function<void(ARGS...)>
的类型(或者更准确地说,std::function<void(int, ...)>
和 void(int)
,它不能,因为推导不考虑隐式转换。
简而言之:指定显式模板参数不会阻止可变函数模板的模板参数推导。
注意:我不是 100% 确定确切的术语,感谢任何语言律师来纠正我!
编辑:我主要基于我阅读的内容 here。
我最初对以下代码片段工作原因的推理是错误的,但由于@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
您能帮我理解为什么参数推导适用于 class 模板而不适用于函数模板吗?
如果我没理解错,class 模板定义了一个函数,所以当我调用时,编译器可能会进行隐式转换,但是对于函数模板,没有函数定义目前,隐式转换没有发生。
但我不明白为什么编译器不能创建函数定义然后应用隐式转换?
#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);
}
错误是:
In function 'int main()':
25:24: error: no matching function for call to 'Test2::add(void (&)(int))'
25:24: note: candidate is:
14:10: note: template void Test2::add(const std::function&)
14:10: note: template argument deduction/substitution failed:
25:24: note: mismatched types 'const std::function' and 'void(int)'
在第一种情况下,您显式实例化了 class 模板 Test1
。这意味着其 add
成员的函数声明是使用签名 add(const std::function<void(int)>&)
生成的。当编译器随后尝试解析 test1.add(func)
时,只有那个候选者。由于 std::function<void(int)>
可以从 void(int)
函数隐式构造,签名匹配,编译器只是实例化成员函数定义,一切都很好。
在第二种情况下,编译器必须执行模板参数 deduction/substitution 以查看它是否可以 "use" add
模板。您可能认为指定 int
会确定模板参数,因此不需要进行推导,但事实并非如此:您的意思可能是部分指定模板参数,例如 here .换句话说,您可能试图用比您明确指定的更多的参数来实例化函数模板,至少编译器不知道您是否这样做。所以它仍然必须尝试匹配 std::function<void(ARGS...)>
的类型(或者更准确地说,std::function<void(int, ...)>
和 void(int)
,它不能,因为推导不考虑隐式转换。
简而言之:指定显式模板参数不会阻止可变函数模板的模板参数推导。
注意:我不是 100% 确定确切的术语,感谢任何语言律师来纠正我!
编辑:我主要基于我阅读的内容 here。
我最初对以下代码片段工作原因的推理是错误的,但由于@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