Search code examples
c++templatesc++11type-deduction

How to deduce the return type of a std::bind object for template use?


I have a class which basically just manages a vector of custom types. To relieve me from writing the same iteration loop over and over again I wrote the following construct:

template<typename T>
uint64_t ACCUMULATE_ON_VECTOR(const std::vector<MyClass*> vec, T method)
{
  return std::accumulate(vec.begin(), vec.end(), 0, [&](uint64_t acc, const MyClass* c)
  {
    return acc + (c ? method(c) : 0);
  });
}

which is then called like this:

ACCUMULATE_ON_VECTOR(_myVec, std::bind(&MyClass::someMethod, std::placeholders::_1));

This works very well in concept but since I have a lot of methods returning different integer types (signed/unsigned, signed/unsigned long), I'd like to abstract away the for now hardcoded uint64_t cause I get compiler warnings all over the place. For this I somehow need to get the return type of the bind object. Can I somehow do this with decltype? What I'm looking for is this:

template<typename T>
<new deduced type> ACCUMULATE_ON_VECTOR(const std::vector<MyClass*> vec, T method)
{
  return std::accumulate(vec.begin(), vec.end(), 0, [&](<new deduced type> acc, const MyClass* c)
  {
    return acc + (c ? method(c) : 0);
  });
}

Solution

  • You can do this using std::result_of:

    template<typename Func>
    typename std::result_of<Func(MyClass*)>::type
    ACCUMULATE_ON_VECTOR(const std::vector<MyClass*> &vec, Func method)
    {
      using ResultType = typename std::result_of<Func(MyClass*)>::type;
      return std::accumulate(vec.begin(), vec.end(), ResultType{},
                             [&](typename std::result_of<Func(MyClass*)>::type acc,
                                 const MyClass* c)
      {
        return acc + (c ? method(c) : ResultType{});
      });
    }
    

    Note I'm value-initializing the return type instead of using the integer literal zero.

    It might be more readable to wrap the function parameter in a std::function<ResultT(MyClass*)>: the accumulate function would be directly templated on the result type, pushing responsibility for that up to the call site.


    BTW, you don't need the auto/trailing return type technique here, because the return type doesn't depend on the argument list, only on the template parameter - nevertheless, I think it looks slightly nicer:

    template<typename Func>
    auto ACCUMULATE_ON_VECTOR(const std::vector<MyClass*> &vec,
                              Func method)
    -> typename std::result_of<Func(MyClass*)>::type
    {