【问题标题】:Why can't my C++ compiler deduce template argument for boost function?为什么我的 C++ 编译器不能为 boost 函数推导出模板参数?
【发布时间】:2011-05-03 23:09:28
【问题描述】:

我这样定义一个方法:

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&lt;void(ArgT)&gt;)' : could not deduce template argument for 'boost::function&lt;void(ArgT)&gt;' from 'anonymous-namespace'::&lt;lambda0&gt;'

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    虽然 C++ lambda 严格来说是单态的,但它们只是函数对象(又名函子)的简写,而且通常函子可以是多态的;即,它们的调用运算符可以被重载或模板化。因此,仿函数(以及因此的 lambda)永远不会隐式转换为模板化的 std::function&lt;&gt;(或 boost::function&lt;&gt;)实例,因为仿函数的 operator() 参数类型不会自动推断。

    换个说法,你的 lambda 表达式的自然类型是一个带有无参数构造函数的函子和一个带有签名 void operator ()(int) constoperator()。不管这个事实对你我来说多么明显,ArgT 应该解析为 int 并不能自动推断,因为 lambdas 是函子,函子的 operator()s 可能会重载和模板化。

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

    【讨论】:

    • 我认为这真的很不幸。让我们希望这个限制可以在下一次迭代中消除。
    • 我尝试使用 bar() 函数调用 foo() 来避免 lambda:结果相同。
    【解决方案2】:

    您希望将 lambda 函数转换为 boost::function&lt;void(ArgT)&gt;,其中将推导出 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);
    } 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-23
      • 1970-01-01
      • 1970-01-01
      • 2018-12-09
      • 1970-01-01
      • 1970-01-01
      • 2017-02-20
      • 1970-01-01
      相关资源
      最近更新 更多