Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

No matching function error when passing lambda function as argument

I have a list of numbers.

I am trying to filter the list and only keep the positive numbers.

I am trying to do it by passing a lambda as an argument.

I wonder why I get function mismatch error.

#include <vector>
#include <algorithm>
#include <functional>

template<typename T>
std::vector<T> keep(
        const std::vector<T> &original,
        std::function<bool(const T&)> useful)
{
    std::vector<T> out;
    for(T item:original)
    {
        if(useful(item))
            out.push_back(item);
    }
    return out;
}

int main()
{
    std::vector<int> a={4,6,2,-5,3,-8,13,-11,27};
    a=keep(a,[](const int& x)->bool{return x>0;});
    for(int y:a)
    {
        std::cout<<y<<std::endl;
    }
    return 0;
}

And this is the error message:

error: no matching function for call to ‘keep(std::vector<int>&, main()::<lambda(const int&)>)’
     a=keep(a,[](const int& x)->bool{return x>0;});
                                                 ^
like image 742
ar2015 Avatar asked Sep 29 '15 09:09

ar2015


1 Answers

Change the function keep to

template<typename T, typename Func>
std::vector<T> keep(const std::vector<T> &original,
                    Func useful)
{
    // code as usual
}

Live example.

This works with an argument to useful being any one of these:

  • lambda
  • std::function
  • functor
  • function pointer

From the documentation:

The lambda expression constructs an unnamed prvalue temporary object of unique unnamed non-union non-aggregate type, known as closure type.

This means that two lambdas with the same code, would generate two different typed objects.

auto f1 = [](int) { return true; };
auto f2 = [](int) { return false; };
f2 = f1;                               // error: no viable '='

However, both of these are implicitly convert-able to the corresponding std::function types:

std::function<bool(int)> fn = f1;
fn = f2;

But then why doesn't it work in your case? This is because of template type deduction. Changing keep to

template<typename T>
std::vector<T> keep(const std::vector<T> &original,
                    std::function<bool(const int &)> useful)
// no type deduction for std::function's template, explicitly mentioned

will make your example compile without any cast at the caller site.

However, trying to match it against std::function<T> won't work since template type deduction doesn't consider any conversion. Template argument deduction looks for exact type matches. Implicit conversions don't matter at this stage. You've to explicitly cast it to a matching std::function as Atomic_alarm comments. Like Joseph says in How to convert a lambda to an std::function using templates:

Template type deduction tries to match the type of your lambda function to the std::function<T> which it just can't do in this case - these types are not the same. Template type deduction doesn't consider conversions between types.

While in the alternative solution what happens is something like this:

auto f = [](int i) { return (i >= 0); }

The type of f here is not std::function but some unnamed type deduced like it would for the template parameter Func above.

If you still want to do it the std::function way, see this answer which does it with an additional template indirection. See this answer and this post for related details.

like image 95
legends2k Avatar answered Nov 08 '22 18:11

legends2k