Anton Kochkov
Anton Kochkov

Reputation: 1287

How to bypass C++ inability to match function types in templates with lambdas

If you have the following template for filtering list:

template <typename T>
inline std::list<T> list_filter(std::list<T> const& a, bool (f)(T)) {
    std::list<T> output;
    std::copy_if(a.begin(), a.end(), std::back_inserter(output), f);
    return output;
}

Then try to call it with the lambda inside like:

std::list<int> lst = {1,2,3,4,5};
auto filtered = list_filter(lst, [](int el) -> bool { return (el % 2 == 0); });

It will produce the error with no matching function for call to list_filter(..., std::__cxx11::list<int>)::<lambda(int)>)'.

Is there any way to bypass that restriction without extracting the lambda into the separate function? Why C++ doesn't allow this obvious pattern?

Upvotes: 2

Views: 225

Answers (2)

songyuanyao
songyuanyao

Reputation: 172964

Implicit conversion (from lambda with no capture-list to function pointer) won't be considered in template argument deduction, which fails to deduce template argument T on the 2nd function argument.

Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.

You can convert lambda to function pointer explicitly by static_cast, or operator+. e.g.

auto filtered = list_filter(lst, +[](int el) -> bool { return (el % 2 == 0); });

Or use std::type_identity (since C++20) to exclude the 2nd function argument from deduction. e.g.

template <typename T>
inline std::list<T> list_filter(std::list<T> const& a, bool (f)(std::type_identity_t<T>)) {
    ...
}

BTW you can make your own type_identity easily if your compiler doesn't support C++20.

Upvotes: 6

Some programmer dude
Some programmer dude

Reputation: 409364

Lambdas with captures are not compatible with pointers to functions (which is what the argument f is).

I recommend that you take a hint from the standard library itself when it comes to callable objects: Use template arguments for the whole callable object:

template <typename T, typename F>
inline std::list<T> list_filter(std::list<T> const& a, F f) {
    std::list<T> output;
    std::copy_if(a.begin(), a.end(), std::back_inserter(output), f);
    return output;
}

Upvotes: 4

Related Questions