Home > OS >  Can function templates be used as first class citizens in higher order function calls?
Can function templates be used as first class citizens in higher order function calls?

Time:10-12

Passing a function template as argument to another function template is always a bit tricky. Typically one has to resort to creating a lambda object that goes and calls the original function.

Example

template <typename It>
void f(It, It) {}

void g(std::vector<int>::iterator, std::vector<int>::iterator) {}

template <typename C, typename F>
void callA(C&& c, F callable) {
  return callable(std::begin(c), std::end(c));
}

Problem

If I have a std::vector<int> c, I cannot just pass f along to callA because f is a template and not a function:

callA(c, f); // f has an unresolved overloaded function type and cannot be deduced
callA(c, std::distance); // same problem
callA(c, g); // works because g is a proper function, not a template
callA(c, [](auto a, auto b) {return f(a,b);}); // works again

Even if we help deduce the type of the callable:

template <typename C, template <typename> typename F,
          typename T = std::decay_t<decltype(std::begin(std::declval<C>()))>>
auto callB(C&& c, F<T> callable) {
  return callable(std::begin(c), std::end(c));
}

this fails to compile.

Question

Is there a way to force C to deduce the function's type directly without resorting to a lambda function or type-erasure like std::function? Effectively, (how) can I turn a function template into a first-class citizen? That is, have its type deduced.

I'm willing to go to some lengths when defining the higher-order function (see callB versus callA), but not when calling it.

CodePudding user response:

You can change the parameter type to function pointer, which could be converted from function template (with template argument deduced). E.g.

template <typename C>
void callA(C&& c, void(*callable) (std::decay_t<decltype(std::begin(std::declval<C>()))>, std::decay_t<decltype(std::begin(std::declval<C>()))>)) {
  return callable(std::begin(c), std::end(c));
}

Then

std::vector<int> c;
callA(c, f); // template argument It will be deduced as std::vector<int>::iterator for f

LIVE

CodePudding user response:

Are you aware of BOOST_HOF_LIFT?

It allows you to lift overloaded and/or templated functions into objects with as easy syntax as auto my_max = BOOST_HOF_LIFT(std::max);.

You could change callA(c, std::distance) to callA(c, BOOST_HOF_LIFT(std::distance)).

  • Related