模板参数扣除失败
Template argument deduction failure
即使在以下程序中对fold_left
的调用似乎足以推断模板函数的参数,但由于模板参数扣除失败而引起的汇编误差。
#include <valarray>
#include <functional>
template<class Input, class Output>
Output fold_left(Output zero, std::function<Output(Output,Input)> op, std::valarray<Input> data)
{
for (auto const& item : data) {
zero = op(zero, item);
}
return zero;
}
#include <iostream>
int main()
{
std::valarray<int> data { 1, 2, 3, 4, 5 };
std::cout << fold_left(0, std::plus<int>{}, data) << "n";
}
main.cpp: In function 'int main()': main.cpp:17:66: error: no matching function for call to 'fold_left(int, std::plus<int>, std::valarray<int>&)' std::cout << fold_left/*<int,int>*/(0, std::plus<int>{}, data) << "n"; ^ main.cpp:5:8: note: candidate: template<class Input, class Output> Output fold_left(Output, std::function<Output(Output, Input)>, std::valarray<_Tp>) Output fold_left(Output zero, std::function<Output(Output,Input)> op, std::valarray<Input> data) ^~~~~~~~~ main.cpp:5:8: note: template argument deduction/substitution failed: main.cpp:17:66: note: 'std::plus<int>' is not derived from 'std::function<Output(Output, Input)>' std::cout << fold_left/*<int,int>*/(0, std::plus<int>{}, data) << "n"; ^
此最小程序只有在调用fold_left
时才会编译:
fold_left<int, int>(0, std::plus<int>{}, data);
这对我来说很奇怪,因为Output
模板参数应该很明显,因为我们提供了int
作为第一个参数(zero
);同样,应正确推导Input
模板参数,因为我们提供了std::valarray<int>
作为第三个参数(data
)期望std::valarray<Input>
。
fold_left
定义中参数的顺序与不相关。
为什么在此处进行模板参数扣除?
,因为std::plus<int>
对于某些类型的O,I
不是std::function<O(O,I)>
的实例,并且它们处于推论的上下文中。
解决方案是用一个新的模板参数替换std::function<O(O,I)>
,该参数本身只是根据两个参数O,I
不可构成的要求,并且返回类型可转换为O
:
template<class Input, class Output, class F,
std::enable_if_t<std::is_convertible_v<
std::invoke_result_t<F&, Output, Input>,
Output>, int> = 0>
Output fold_left(Output zero, F op, std::valarray<Input> data)
{ ... }
您可以在非循环上下文中分别包装op
参数,但实际上您实际上不需要std::function
提供的类型删除,因此它是不确定的。
问题是std::function<Output(Output,Input)>
无法正确推断其模板参数,因为std::plus<int>
不是std::function
。
类型 - 否决仅在模板类型可以与所传递的参数匹配时有效;它不会执行通过转换/构造进行的转换;而是将类型全部视为。
在这种情况下, std::plus<int>
是不是 a std::function<int(int,int)>
,它是转换为 std::function<int(int,int)>
。这是导致扣除额失败的原因。
替代方案是用别名打破对类型降低的贡献,明确传递std::function
类型,或者接受该函数作为不同的模板参数(实际上可能更有效)
1。通过别名
对类型降低的贡献进行中断贡献如果您成为类型的别名,以使类型不构成扣除,那么它可以留下第一个和第三个参数来确定std::function
的类型
(在我的头顶上;未经测试)
template<typename T>
struct nodeduce{ using type = T; };
template<typename T>
using nodeduce_t = typename nodeduce<T>::type;
template<class Input, class Output>
Output fold_left(Output zero, std::function<nodeduce_t<Output>(nodeduce_t<Output>,nodeduce_t<Input>)> op, std::valarray<Input> data)
{
....
}
这是一种丑陋的方法,但是它将禁用std::function
从贡献中,因为类型是通过nodeduce_t
转换的。
2。通过std::function
这是最简单的方法;您的通话代码将成为:
fold_left(0, std::function<int(int,int)>(std::plus<int>{}), data);
诚然,它不是很漂亮 - 但是它可以解决扣除,因为std::function<int(int,int)>
可以直接匹配输入。
3。将作为模板参数传递
这实际上具有一些额外的性能好处;如果您接受该参数为模板类型,则它还允许编译器更好地将您的函数插入(std::function
都有问题)
template<class Input, typename Fn, class Output>
Output fold_left(Output zero, Fn&& op, std::valarray<Input> data)
{
for (auto const& item : data) {
zero = op(zero, item);
}
return zero;
}
所有这些都是可以完成相同任务的替代方法。还值得一提的是,可以使用SFINAE与enable_if
或decltype
评估这样的事情进行编译时验证:
template<class Input, typename Fn, class Output, typename = decltype( std::declval<Output> = std::declval<Fn>( std::declval<Output>, std::declval<Input> ), void())>
Output fold_left( ... ){ ... }
decltype(...)
语法确保表达式构成良好,如果是,则允许此函数用于超载分辨率。
编辑:将decltype
评估更新为测试Output
和Input
类型。使用C 17,您也可以在enable_if
中使用std::is_convertible_v<std::invoke_result_t<F&, Output, Input>,Output>
,如Barry在其他答案中所建议的。
这对我来说很奇怪,因为
Output
模板参数应该很明显,因为我们提供了int
作为第一个参数
编译器将尝试解决Output
的每个位置。如果来自不同参数的推论类型不匹配,那么您将获得歧义,这将导致扣除次数失败。
由于您在Output zero
中使用Output
,并且std::function<Output(Output,Input)> op
编译器将使用0
和std::plus<int>
来尝试找出Output
是什么。 std::plus<int>
不是std :: function",因此无法弄清楚类型,这就是替代失败的原因。
如果将std::plus<int>{}
施加到std::function
中,则编译器可以推断模板类型
std::cout << fold_left(0, std::function<int(int, int)>(std::plus<int>{}), data) << "n";
,如果您不想这样做,尽管您需要将功能作为通用类型,然后使用Sfinae来约束模板,例如Barry提供的答案。
- 模板参数替换失败,并且未完成隐式转换
- 具有默认模板参数的多态类的模板推导失败
- 视图中的参数推导失败:take_while
- variadic模板中的模板参数推导失败
- 当给定默认值时,为什么此模板参数推导失败
- 模板参数推导失败,函数参数/参数不匹配
- 为什么模板参数推导失败?
- 嵌套参数包扩展失败
- 模板参数的 C++ 自动模板推导失败
- 调试模板时出现问题.专门针对 Linux GCC 7、GCC 6、GCC 5、GCC 4.9 错误构建失败:模板参数 1
- 使用显式模板参数列表和 [temp.arg.explicit]/3 的函数调用的演绎失败
- 失败,出现错误 87:参数不正确.如何判断哪个参数不正确?
- 在 g++ 6.2.1 中将参数包转发到 constructor() 失败
- 当变量和参数名称匹配时,移动语义构造失败
- 作为模板参数的成员函数指针在继承的成员函数上失败,如何以及为什么?
- 模板参数推导/替换失败,lambda作为函数指针
- 调用基函数时模板参数推导失败
- 类模板参数推导失败会导致替换失败
- 折叠表达式模板参数推导/替换失败
- 结果失败或多个参数无效