2

Consider following code

template<typename T>
T modify(const T& item, std::function<T(const T&)> fn)
{
    return fn(item);
}

When trying to use it as modify(5, [](const int& i){return 10*i;}); it fails to compile with

could not deduce template argument for 'std::function<T(const T &)> from lambda

I know that compiler can not deduce T from lambda, because lambda is not std::function, but isn't T already deduced from 5?

I can get over it using

template<typename T, typename F>
T modify(const T& item, const F& functor)
{
    return functor(item);
}

for which previous example compiles, but it is in my opinion less intuitive. Is there a way to let the function argument to remain std::function and have it's template argument deduced automatically from item?

101010
  • 41,839
  • 11
  • 94
  • 168
Zereges
  • 5,139
  • 1
  • 25
  • 49
  • 1
    Using a separate template argument for the "predicate" is how the standard library does it. If you look at all [algorithmic functions in the standard library](http://en.cppreference.com/w/cpp/algorithm) you will see that all that takes a predicate takes it as a separate templated type. However all those functions are well specified and documented, making the "intuitive" point moot. Good documentation is the key here IMO. – Some programmer dude Jul 12 '16 at 15:57
  • `std::function` is not an efficient solution when the functor type is known. Concepts will eventually solve the intuition problem. – Oktalist Jul 12 '16 at 22:44

2 Answers2

4

What you basically want to do is prevent deduction from happening. If template deduction occurs, it will fail (because a lambda is not a std::function<> - it doesn't matter that T was deduced from the first argument, deduction must succeed in every argument that is a deduced context). The way to prevent deduction is to stick the entire argument in a non-deduced context, the easiest way of doing that is to throw the type into a nested-name-specifier. We create such a type wrapper:

template <class T> struct non_deduce { using type = T; };
template <class T> using non_deduce_t = typename non_deduce<T>::type;

And then wrap the type in it:

template<typename T>
void foo(const T& item, std::function<void(T)> f);

template<typename T>
void bar(const T& item, non_deduce_t<std::function<void(T)>> f);

foo(4, [](int ){} ); // error
bar(4, [](int ){} ); // ok, we deduce T from item as int,
                     // which makes f of type std::function<void(int)>

Note, however, that:

template <typename T, typename F>
void quux(const T&, F );

is not really any less readable, and strictly more performant.

Barry
  • 286,269
  • 29
  • 621
  • 977
3

You can do it by using the identity trick as below:

template <typename T>
struct identity {
  typedef T type;
};

template<typename T>
T modify(const T& item, typename identity<std::function<T(const T&)>>::type fn) {
  return fn(item);
}

Live Demo

101010
  • 41,839
  • 11
  • 94
  • 168