问题描述
这是我的代码:
#include<iostream>
struct Item {
int val;
};
struct XItem {
int val;
};
void transform(const Item &i,XItem &target) {
target.val = i.val;
}
template<typename T>
void show(const T &v) {
std::cout << v.val << std::endl;
}
template<typename ParamsType,typename ResultType>
void handleRequest(Item &cur,ResultType (*impl)(const ParamsType &p)) {
ParamsType p{};
transform(cur,p);
ResultType res = (*impl)(p);
show(res);
}
struct ResItem {
int val;
};
int main(int argc,char *argv[]) {
Item i{42};
handleRequest(i,[](const XItem &x) {
return ResItem{x.val};
});
return 0;
}
编译它会出现以下错误:
test.cpp:33:3: error: no matching function for call to 'handleRequest'
handleRequest(i,[](const XItem &x) {
^~~~~~~~~~~~~
test.cpp:21:6: note: candidate template ignored: could not match 'ResultType
(*)(const ParamsType &)' against '(lambda at test.cpp:33:20)'
void handleRequest(Item &cur,ResultType (*impl)(const ParamsType &p)) {
^
我不确定为什么会发生这种情况,但是我确实怀疑,因为lambda虽然可以隐式转换为,但它不是一个,所以无法从中推导出模板参数
我尝试改用std::function<ResultType(const ParamsType &p)>
,这也不起作用。 This question详细说明了问题,因此我尝试使用其解决方案:
template<typename ParamsType,typename ResultType,typename Callback>
void handleRequest(Item &cur,Callback cb) {
ParamsType p = transform(cur);
ResultType res = std::invoke(cb,p);
show(res);
}
但是,现在ParamsType
和ResultType
不能从回调中隐式推导出,我需要显式地给它们。但是我真的想推断ResultType
,因为在实际代码中,它可能会很长(它是从lambda return
语句推断出来的,该语句比这个最小的示例更复杂)。我需要这两种类型,因为transform
和show
都被重载,并且需要绑定类型。
在这种情况下是否可以让handleRequest
推断这些类型?如果是,怎么办?
解决方法
问题在于,template argument deduction中未考虑隐式转换(从lambda到函数指针),这无法推断出模板参数。
类型推导不考虑隐式转换(上面列出的类型调整除外):这是overload resolution的工作,以后会发生。
您可以显式指定模板参数以绕过推论,
handleRequest<XItem,ResItem>(i,[](const XItem &x) {
return ResItem{x.val};
});
或将lambda显式转换为函数指针,
handleRequest(i,static_cast<RestItem(*)(const XItem&)>([](const XItem &x) {
return ResItem{x.val};
}));
或使用operator+
将lambda转换为函数指针。
handleRequest(i,+[](const XItem &x) {
return ResItem{x.val};
});