【问题标题】:bindParameter function with variadic templates in C++11C++11 中带有可变参数模板的 bindParameter 函数
【发布时间】:2011-11-17 12:50:42
【问题描述】:

我正在尝试编写一个简单的函数来转换 std::function 对象,同时绑定最后一个参数。这就是我所拥有的:

template<typename R, typename Bind, typename ...Args> std::function<R (Args...)> bindParameter (std::function<R (Args..., Bind)> f, Bind b)
{
    return [f, b] (Args... args) -> R { return f (args..., b); };
}

这就是我想使用它的方式:

int blub (int a, int b)
{
    return a * b;
}

// ...

int main ()
{
    std::function<int (int, int)> f1 (blub);

    // doesn't work
    std::function<int (int)> f2 = bindParameter (f1, 21);

    // works
    std::function<int (int)> f3 = bindParameter<int, int, int> (f1, 21);

    return f2 (2);
}

...所以在这个例子中主函数应该返回 42。问题是,gcc (4.6) 似乎没有正确推断模板参数的类型,第一个版本产生以下错误:

test.cpp:35:58: error: no matching function for call to 'bindParameter(std::function<int(int, int)>&, int)'
test.cpp:35:58: note: candidate is:
test.cpp:21:82: note: template<class R, class Bind, class ... Args> std::function<R(Args ...)> bindParameter(std::function<R(Args ..., Bind)>, Bind)

但在我看来,参数是显而易见的。或者这种类型推断没有被标准覆盖或者还没有在 gcc 中实现?

【问题讨论】:

    标签: c++ templates c++11 bind inference


    【解决方案1】:

    您不能将std::function 用作函数模板的推导参数。由于没有规则可以将int(*)(int, int)std::function&lt;int(int, int)&gt; 匹配,因此无法以这种方式进行扣除。 (还要考虑对于 any std::function&lt;Signature&gt; 有一个接受 int(*)(int, int) 的构造函数,即使在大多数情况下这会在实例化时导致错误。)

    一般情况下检测函子的签名是有问题的。即使是 KennyTM 的解决方案也有局限性:它检测单态函子和类似函数的东西的签名,但不适用于多态函子(例如重载 operator())或具有代理调用函数的函子(即使在单态情况下)。

    然而,感谢decltype(或等效的std::result_of),可以完全回避检测签名的问题,我建议这样做。因此,KennyTM 答案的变体:

    template<typename Functor, typename Bound>
    struct bind_last_type {
        Functor functor;
        Bound bound;
    
        template<typename... Args>
        auto operator()(Args&&... args)
        -> typename std::result_of<Functor&(Args..., Bound)>::type
        // equivalent:
        // -> decltype( functor(std::forward<Args>(args)..., std::move(bound)) )
        { return functor(std::forward<Args>(args)..., std::move(bound)); }
    };
    
    template<typename Functor, typename Bound>
    bind_last_type<
        typename std::decay<Functor>::type
        , typename std::decay<Bound>::type
    >
    bind_last(Functor&& functor, Bound&& bound)
    { return { std::forward<Functor>(functor), std::forward<Bound>(bound) }; }
    

    【讨论】:

    • 谢谢,我不知道任何 std::function 的构造函数都可以接受任何带有另一个签名的可调用对象(似乎在内部抛出错误......)。这也让我对 KennyTM 的问题变得多余。
    【解决方案2】:

    不确定推理,但如果我只定义一个模板函数对象,它就可以工作。

    template <typename FType, typename LastArgType>
    struct BindLastHelper
    {
        FType _f;
        LastArgType _last_arg;
    
        template <typename... Args>
        typename utils::function_traits<FType>::result_type
            operator()(Args&&... args) const
        {
            return _f(std::forward<Args>(args)..., _last_arg);
        }
    };
    
    template<typename FType, typename LastArgType>
    BindLastHelper<FType, LastArgType> bindParameter (FType f, LastArgType b)
    {
        return BindLastHelper<FType, LastArgType>{f, b};
    }
    

    注意:

    【讨论】:

    • 谢谢你,就像一个魅力!但是是否可以转换bindParameter 的返回值(通过额外的辅助函数或其他东西),以便我可以编写类似auto f3 = bindParameterAnotherHelper (f2, int); 的东西,以便f3 自动成为std::function&lt;int (int)&gt; 类型的对象?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-14
    • 1970-01-01
    • 1970-01-01
    • 2011-12-14
    相关资源
    最近更新 更多