How can I restrict lambda signatures in C++17 template arguments?

Viewed 258

Suppose I have two functions that receive different lambda types as arguments:

template<typename F>
void func1(F&& lambda) {
    // lambda must be [](unsigned int) -> short
}

template<typename F>
void func2(F&& lambda) {
    // lambda must be [](const vector<string>&) -> void
}

How can I restrict these lambda signatures in C++17, to match exactly what I need in each case?

2 Answers

Pre C++20:

template<typename F, typename = std::enable_if_t<
    std::is_invocable_v<F, unsigned int>
>>
void  func1( F &&lambda ) {
    // lambda must be [](unsigned int) -> short
}

C++20:

template<typename F>
requires std::is_invocable_v<F, unsigned int>
void  func1( F &&lambda ) {
    // lambda must be [](unsigned int) -> short
}

Use std::is_invocable_r_v if you want to validate return type as well.

[EDIT]

... using std::is_invocable_r_v

template<typename F, typename = std::enable_if_t<
    std::is_invocable_r_v<short, F, unsigned int>
>>
void  func1( F &&lambda ) {
    // lambda must be [](unsigned int) -> short
}

A way that immediately pops to mind is to leverage std::function and its deduction guides. Using CTAD, the signature can be deduced automatically for certain types of functors (non-generic lambdas among them). It will look like this

template<typename F>
auto func1(F&& lambda) 
  -> std::enable_if_t<std::is_same_v<decltype(std::function{std::forward<F>(lambda)}), std::function<short(unsigned)>>> {
    // lambda must be [](unsigned int) -> short
}

std::function{std::forward<F>(lambda)} is a functional cast expression that attempts to convert the argument into a std::function whose template arguments get deduced. If the deduction is successful, we can obtain a type to compare against std::function<short(unsigned)>. With that in hand, we can use the usual SFINAE utilities in the standard library to constrain func1.

Mind however, that this doesn't constrain the function template only to lambdas, any functor for which the CTAD succeeds, and then matches the argument list, is going to be accepted.

Related