Question

I'm trying to write a simple function to convert a std::function<> object while binding the last parameter(s). That's what I've got:

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); };
}

And that's how I'd like to use it:

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);
}

... so that in this example the main function should return 42. The problem is, that gcc (4.6) doesn't seem to infer the types of the template parameters correctly, the first version produces the following errors:

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)

But in my opinion the parameters are obvious. Or is this kind of type inference not covered by the standard or not yet implemented in gcc?

Was it helpful?

Solution

You can't use std::function as a deduced parameter of a function template. Deduction can't work in this fashion as there are no rules to match int(*)(int, int) to std::function<int(int, int)>. (Consider also that for any std::function<Signature> there is a constructor accepting int(*)(int, int), even if in most cases this results in an error when instantiated.)

It's problematic to detect the signature of functor in the general case. Even KennyTM's solution has limitations: it detects the signature of monomorphic functors and function-like things, but won't work for polymorphic functors (e.g. with overloaded operator()) or functors with surrogate call functions (even in the monomorphic case).

It is however possible to completely sidestep the issue of detecting the signature thanks to decltype (or equivalently, std::result_of), and I would recommend doing so. Hence, a variant on KennyTM's answer:

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) }; }

OTHER TIPS

Not sure about the inference, but it works if I just define a templated function object.

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};
}

Note:

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top