2

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?

3
  • Why do you need to specify the signature for your parameter? Just use a separate template parameter for the callable. Commented Sep 1, 2020 at 7:02
  • @super for clarity. To avoid the misuse. Commented Sep 1, 2020 at 7:12
  • 1
    When the callable is passed to copy_if it needs to have the right signature, and if not will produce a compile time error. You don't really gain anything in that regard. Commented Sep 1, 2020 at 7:15

2 Answers 2

6

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.

Sign up to request clarification or add additional context in comments.

6 Comments

That is interesting engineering: std::type_identity_t.
@Const See non-deduced context for more.
@AntonKochkov Yes, caputring lambdas can't convert to function pointer. You can change the function parameter from function pointer to std::function, (same deduction issue as function pointer, solution above is required too, i.e. use std::function<bool (std::type_identity_t<T>)>), or as @someprogrammerdude suggested pass the lambda directly.
@songyuanyao Could you please give some information about this line: +[](int el) -> bool { return (el % 2 == 0); }
@Const If you're interested, I recently wrote a brief blog post on particularly the use of type identity mapping for controlling template argument deduction by removing arguments by placing them in non-deduced contexts.
|
4

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; } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.