如果模板没有可变参数,Lambda表达式将被推导为std::function。

12
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有可变参数。但不知何故,编译器能够将bar中的lambda转换为std::function。

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


6
根据使用情况和需求,我建议你不要使用std::function作为参数,而是看看标准库如何仅使用单个模板参数处理可调用对象。 - Some programmer dude
2个回答

10

您不需要为 bar 的类型参数做出任何推断,因为它们已经被完全指定。

对于 foo,您仍然需要推断剩余的部分,但是由于 lambda 不是 std::function,所以推断失败。


2
当尝试查找适当的std :: function构造函数进行参数类型转换时,可能会对调用bar进行某种类型推断。只是没有直接找到正确的bar。 - PeterT
1
@PeterT 不是“类型推导”,而是“类型转换”。它们是编译过程中发生在不同阶段的两个不同概念。 - Richard Hodges
@RichardHodges 但在类型转换期间,std::function构造函数中会发生一些类型推导。 - PeterT

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

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

它没有完全指定ParamT。实际上,没有办法完全指定ParamT

作为一个不完全指定的模板,会发生推导失败的情况。它不会尝试“假设包不再继续扩展”的方法。

您可以通过以下方式进行修复:

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 这样的类型擦除类型通常是代码异味。

将两者替换为:

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

如果您必须获取参数,请使用函数特征助手(SO 上有许多)。如果您只需要返回值,则已经有 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);
}

使用 的推导指南特性。

你能否澄清为什么“推断类型擦除类型(如std::function)的类型”通常是代码异味? - pong
1
类型擦除和类型推断是粗略的反向操作。同时进行两者就像是对 int*const* 进行无条件解引用。调用站点处的 & 和使用站点处的无条件 * 都表示问题;将类型推导到要擦除的类型也是如此。 - Yakk - Adam Nevraumont

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接