4

I am trying to match callable objects in C++ templates and provide different implementations based on the type of callable object that is receives as an argument.

I have the following template functions:

template<class Ret, class... Args>
void fun(std::function<Ret(Args...)> f) {}

template<class Ret, class... Args>
void fun(Ret(*f)(Args...)) {}

They accept std::function objects and ordinary function pointers, and correctly deduce their return Ret and argument Args types. However, I am having difficulty passing anonymous lambda expressions as they do not match any of the templates above.

Calling something like

fun([](int x, int y){return x + y;})

results in a compilation error. What is the correct way to implement a template expression that accepts anonymous lambda functions? Maybe one way is to check if the class that is being passed has an operator() method?

D R
  • 21,936
  • 38
  • 112
  • 149

2 Answers2

3

Conversions happen after template argument substitution. The type of a lambda is not std::function, but an anonymous type. Indeed, you'll have the receive an anonymous lambda in order to receive any callable object.

It seems your concern is about restricting the object received to have the operator() defined. This problem is easily solvable with expression sfinae. However, since you're not receiving parameters, it will be harder to check if the expression is valid.

This is not a general example, but it will work for any lambda that does not have auto as one of it's parameter, or any object that has operator() not overloaded:

template<typename F>
auto fun(F f) -> void_t<decltype(&T::operator())> {
    // ...
}

With void_t implemented as follow:

template<typename...>
using void_t = void;

The expression inside the decltype must be valid in order for the function to exist.

If you want your code to work with more types, you'll have to offer an overload that you can specify the parameters. The check is now trivial to implement:

// Another overload of your function
template<typename F, typename... Args>
auto fun(F f) -> void_t<decltype(f(std::declval<Args>()...)> {
    // ...
}

That way, it will works for generic lambda and overloaded operators too.

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
2

This is valid C++ code, and gcc 6.1.1 at C++14 level will back me up on this:

template<typename F>
void foo(F &&f)
{
}

void bar()
{
    auto lambda=[](auto a, auto b) {};

    foo(lambda);
}

As you can see, a template can't possibly deduce the arguments of a lambda. The argument types to a lambda are not known until they are used.

You can deduce that something has an operator(), as the other answer shows, but that's pretty much as far as it goes.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • I'm not sure I understand what you mean. Why is this not an example of deducing the arguments of a lambda: http://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda. – Nir Friedman Sep 27 '16 at 03:34