为什么我的C++编译器不能推导出boost函数的模板参数?

mis*_*why 9 c++ templates

我定义了一个像这样的方法:

template <class ArgT>
void foo(ArgT arg, ::boost::function< void(ArgT) > func)
{
    func(arg);
}
Run Code Online (Sandbox Code Playgroud)

并像这样使用 - 例如 - :

foo(2, [](int i) -> void { cout << i << endl; });
Run Code Online (Sandbox Code Playgroud)

为什么编译器不能推断出类型,因为它肯定是一个int

我得到'void foo(ArgT,boost::function<void(ArgT)>)' : could not deduce template argument for 'boost::function<void(ArgT)>' from 'anonymous-namespace'::<lambda0>'.

ild*_*arn 10

虽然C++ lambdas是严格单态的,但它们只是函数对象(aka functor)的简写,而且一般来说,functor可以是多态的; 即,他们的呼叫运营商可能过载或模板化.因此,仿函数(以及lambdas)永远不会隐式转换为模板std::function<>(或boost::function<>)实例,因为operator()仿函数的参数类型不能自动推断.

为了略微区别它,lambda表达式的自然类型是一个带有无参数构造函数和operator()带签名的仿函数void operator ()(int) const.然而,显而易见的事实可能是你和我,它不能自动推断ArgT应该解决,int因为lambdas是仿函数和仿函数operator()可以重载和模板.

TL; DR:你想要的是不可能的.


yme*_*ett 7

你想从lambda函数的转换boost::function<void(ArgT)>,其中ArgT的被推导出来.作为一般规则,您不能在函数的同一参数中进行类型推导和转换:在推导模板参数时不会发生转换.

这背后的原因如下.这里涉及三种类型:(1)模板参数,(2)函数参数类型,(3)传递的对象类型.其中两种类型(1和2)可以相互推断,但两者都是未知的.如果编译器可以假设2和3是相同的类型,问题就解决了,但是如果所有编译器都知道3可以转换为2,则可能存在任意数量的可能解决方案,并且编译器不希望解决问题.在实践中,我们知道在这种特殊情况下只有一种可能的解决方案,但标准并没有对案例进行区分.

上述规则适用于所有可推导的上下文,即使模板参数可以从另一个函数参数推导出来.这里的解决方案是使相关的函数参数成为不可推导的上下文,即编译器永远不会尝试从函数参数推导出模板参数的上下文.这可以按如下方式完成:

template <class T> struct identity { typename T type; };

template <class ArgT>
void foo(ArgT arg, typename identity<::boost::function<void(ArgT)>>::type func)
{
  func(arg);
} 
Run Code Online (Sandbox Code Playgroud)