Search code examples
c++templatesc++14generic-programming

Auto deduce type of function in template


I have simple map implementation and simple id (identity):

template <typename T>
T map(const T& x, std::function<decltype(x[0])(decltype(x[0]))> f) {
    T res(x.size());
    auto res_iter = begin(res);
    for (auto i(begin(x)); i < end(x); ++i) {
        *res_iter++ = f(*i);
    }
    return res;
}

template <typename T>
T id(T& x) {return x;}

and when I call is as

vector<int> a = {1,2,3,4,5,6,7,8,9};
map(a, id<const int>);

it works, but I want call it without type specification, like this:

map(a, id);

and when I do it, I get error:

error: cannot resolve overloaded function 'id' based on conversion to type 'std::function<const int&(const int&)>'
 map(a, id);
          ^

How can I resolve it and why can't compiler deduce type of id from its context in map when error contains right bounded type?


Solution

  • If you are in a C++14 compliant environment, there is a very clean way to do this. Instead of using std::function and a templated class, use an unconstrained forwarding reference and a generic lambda as follows:

    #include <vector>
    
    template <typename T,typename F>
    T map(const T& x, F &&f) {
      T res(x.size());
      auto res_iter = begin(res);
      for (auto i(begin(x)); i < end(x); ++i) {
        *res_iter++ = f(*i);
      }
      return res;
    }
    
    auto id = [](auto x) { return x;};
    
    int main()
    {
      std::vector<int> v = {1, 2, 3, 4};
      auto v2 = map(v, id);
    }
    

    In C++11, you would have to replace the generic lambda with a functor whose operator() is a templated method, as follows:

    struct {
      template<typename T>
      T operator()(T x) const
      {
        return x;
      }
    } id;
    

    In C++98 syntax you will not be able to use the forwarding reference, so you will have to consider copying and functor mutability issues.