Search code examples
c++c++11templatesstd-functionstdbind

C++ template function from callable class instance to std::function


I do need std::function for stateful operation.

So I would like to make an instance of a class implementing operator() into std::function. I did test std::bind works, but can't wrap it up as a function.

struct StatefulOperation
{
    StatefulOperation(int num) : num_(num) {}
    int operator()(int i)
    {
        num_ += i;
        return num_;
    }
    
    int num_;
};


// This works

const StatefulOperation obj(40);
    
using std::placeholders::_1;
std::function<int(int)> op = std::bind(&StatefulOperation::operator(), obj, _1);


// Want something like this

template<typename A, typename B, typename C>
std::function<B(A)> op(C& obj);


// This doesn't work

template<typename A, typename B, typename C>
std::function<B(A)> op(C& obj)
{
    using std::placeholders::_1;
    return std::bind(&C::operator(), obj, _1);
}

The last implementation attempt fails with error like below;

main.cpp:52:21: note:   template argument deduction/substitution failed:
main.cpp:75:35: note:   couldn’t deduce template parameter ‘A’
   75 |     std::function<int(int)> f = op(a);
      |                                 ~~^~~

How can I get this done?


Solution

  • The compiler cannot deduce A and B just from the return type. You need to specify them:

    std::function<int(int)> f = op<int, int>(a);
    

    However, you don't really need these additional template parameters. Just use auto to deduce the return type:

    template<typename C>
    auto op(C& obj)
    {
        using std::placeholders::_1;
        return std::bind(&C::operator(), std::ref(obj), _1);
    }
    

    The return type will not be exactly the same, but convertible to what you want (std::function<B(A)>). If you want to specify the exact return type, you can do so with a combination of this and std::invoke_result_t.