如何让 C++ 从 lambda 推断模板类型参数?

How can I make C++ infer template type parameters from a lambda?

这是我的代码:

#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)> 代替,这也不起作用。 详细说明了问题,所以我尝试使用它的解决方案:

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);
}

但是,现在 ParamsTypeResultType 不能从回调中隐式推导出来,我需要明确地给出它们。但我真的很想推断 ResultType 因为在实际代码中,它可能会很长(它是从 lambda return 语句推断出来的,它比这个最小的例子更复杂)。我需要这两种类型,因为 transformshow 都已重载并且需要绑定类型。

在这种情况下是否可以 handleRequest 推断出这些类型?如果可以,如何推断?

问题是,template argument deduction 中没有考虑隐式转换(从 lambda 到函数指针),这无法推导模板参数。

Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.

您可以明确指定模板参数以绕过推导,

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};
});