Home > Net >  How can I define a function that takes a lambda with capture as parameter?
How can I define a function that takes a lambda with capture as parameter?

Time:09-17

I have this function that returns true if any of the elements in the list match the given predicate

bool _any(bool (*predicate)(MyStructure), list<MyStructure> arr)
{
    for (int i = 0; i < arr.size(); i  )
    {
        if (predicate(arr.front()))
            return true;
        arr.pop_front();
    }
    return false;
}

This works for very simple lambdas, but if the given lambda needs to capture this then I have an error which I can't figure out how to fix.

Assert::IsTrue(_any(
    [this](MyStructure t)
    {
        return t._name == "NAME_SEARCHED" &&
            t._type == "TYPE_SEARCHED" &&
            _any([](OtherStruct o) { return o._name == "SEARCHED_2"; }, t._children);
    },
    myList));

Error:

cannot convert argument 1 from 'UTests::<lambda_e8bda0383e9f0c2ae44be631a7424852>' 
to 'bool (__cdecl *)(MyNameSpace::MyStructure)'

(N.B. : _any that takes an OtherStruct is defined as well).

CodePudding user response:

You can not convert the stateful lambda to function pointer!

The problem is, that _any function expects a typed function pointer of type bool (*)(MyStructure) not a lambda function. You could have converted the lambda to a function pointer, if it would have been a capture-less lambda.

That means, here

Assert::IsTrue(_any(
[this](MyStructure t)
//^^^^ ---> capture the "this"
{
   // ...
},
myList));

you are trying to convert a lambda function(with capturing the instance) to a typed function pointer. This is simply not possible, hence, the compiler error.


I can't figure out how to fix.

Make the _any a template function, so that the compiler can do the deduction for you.

template<typename Callable>
bool _any(Callable predicate, std::list<MyStructure> const& arr)
{
   // ...
    return false;
}

or using std::function, with some type erasure overhead

#include <functional> // std::function

bool _any(std::function<bool(MyStructure)> const& predicate
    , std::list<MyStructure> const& arr)
{
   // ...
    return false;
}

CodePudding user response:

Just use a template argument as in std::for_each().

By the way, I'm not certain passing a list by value and consuming it is necessary.

template<typename Predicate>
bool _any(Predicate predicate, const list<MyStructure> &arr)
{
    for(const auto &elem: arr)
    {
        if (predicate(elem))
            return true;
    }
    return false;
}

Finally, you could rely on std::any_of().

template<typename Predicate>
bool _any(Predicate predicate, const list<MyStructure> &arr)
{
    return std::any_of(cbegin(arr), cend(arr), predicate);
}

Look at the existing functionalities of <algorithm>, almost « everything » already exists and can be adapted to specific contexts thanks to the lambda-closures expected as template arguments.

In general, std::function should only be used when the closure needs to be stored in order to be called later (thread, callback...). If the closure is directly used in the function, and no longer needs to exist when the function returns, then the template solution is preferred because it saves a bit of memory and efficiency.

  • Related