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

时间:2011-05-03 14:30:06

标签: c++ templates

我定义了一个类似的方法:

template <class ArgT>
void foo(ArgT arg, ::boost::function< void(ArgT) > func)
{
    func(arg);
}

并像这样使用 - 例如 - :

foo(2, [](int i) -> void { cout << i << endl; });

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

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

2 个答案:

答案 0 :(得分:10)

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

要略微区分它,lambda表达式的自然类型是一个带有无参数构造函数的函子和一个带有签名operator()的{​​{1}}。然而,这个事实可能对你和我来说显而易见,void operator ()(int) const应该解析为ArgT并不是自动推断的,因为lambdas是仿函数和仿函数'int可以重载和模板。< / p>

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

答案 1 :(得分:8)

您希望从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);
}