如果模板没有可变参数,则 Lambda 推导为 std::function

Lambda is deduced to std::function if template has no variadic arguments

template<typename ReturnT, typename... ParamT>
void foo(std::function<ReturnT(ParamT...)> callback)
{}

template<typename ReturnT, typename ParamT>
void bar(std::function<ReturnT(ParamT)> callback)
{}

main()
{    
    foo<int, int>([](int x){ return x; });  // no instance of function 
                                            //   template matches argument list
    bar<int, int>([](int x){ return x; });  // OK
}

foobar 之间的唯一区别是 foo 具有可变参数。不知何故,编译器能够将 lambda 转换为 std::function in bar.

据我了解,模板类型推导不考虑类型转换。那么不应该都失败吗?

bar的类型参数没有任何推导,它们是完全指定的。

您在 foo 中仍有尾部要推断,但失败了,因为 lambda 不是 std::function

template<typename ReturnT, typename... ParamT>
void foo(std::function<ReturnT(ParamT...)> callback)
{}

现在,foo<int,int>foo<ReturnT=int, ParamsT starts with {int}>

它没有完全说明 ParamT。其实没有办法完全指定ParamT.

作为未完全指定的模板,推演发生,推演失败。它不会尝试 "what if I just assume the pack doesn't go any further".

您可以通过以下方式解决此问题:

template<typename ReturnT, typename... ParamT>
void foo(block_deduction<std::function<ReturnT(ParamT...)>> callback)
{}

其中 block_deduction 看起来像:

template<class T>
struct block_deduction_helper { using type=T; }:
template<class T>
using block_deduction = typename block_deduction_helper<T>::type;

现在对 foo 的第一个参数的推导被阻止。

并且您的代码有效。

当然,如果你传入一个std::function,它将不再自动推导参数。

请注意,推断像 std::function 这样的 a 类型擦除类型的类型通常是代码味道。

将两者替换为:

template<class F>
void bar(F callback)
{}

如果您必须获取参数,请使用函数特征助手(SO 上有很多)。如果您只需要 return 值,std 特征已经可以解决这个问题。


中你可以这样做:

tempate<class R, class...Args>
void bar( std::function<R(Args...)> f ) {}
template<class F>
void bar( F f ) {
  std::function std_f = std::move(f);
  bar(std_f);
}

使用 推理指南功能。