Reputation: 5209
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 &)>
fromlambda
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
?
Upvotes: 2
Views: 130
Reputation: 302748
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.
Upvotes: 4
Reputation: 42899
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);
}
Upvotes: 3