Chris Crozz
Chris Crozz

Reputation: 183

How to use lambda, returning a bool, as parameter

I am trying to pass a lambda as parameter to a function that specifies if items should be included in a new vector.

At the moment I am using templates for the vector type (T) and the lambda expression (Func). But how can I make the function to take a lambda with T as parameter and bool as returning type?

This is my code actual:

#include <string>
#include <vector>

using namespace std;

// DTO with some members
struct DTO
{
    int Id;
    string Name;
    string Note;
};

// Function to receive a new vector, containing the searched items
template<typename T, typename Func>
vector<T> Where(vector<T> input, Func lambda)
{
    vector<T> v;
    for (auto it = input.begin(); it != input.end(); ++it)
        if (lambda(*it))
            v.push_back(*it);
    return v;
}

int main()
{
// Some test data
    DTO dto1;
    dto1.Id = 1;
    dto1.Name = "Test";
    dto1.Note = "asdasfa";
    DTO dto2;
    dto2.Id = 2;
    dto2.Name = "Test";
    dto2.Note = "asdasfa";
    DTO dto3;
    dto3.Id = 2;
    dto3.Name = "Test2";
    dto3.Note = "asdasfa";
    DTO dto4;
    dto4.Id = 2;
    dto4.Name = "Test2";
    dto4.Note = "asdasfa";
    DTO dto5;
    dto5.Id = 2;
    dto5.Name = "Test2";
    dto5.Note = "123";

    vector<DTO> numbers2 = 
    { 
        dto1,dto2,dto3,dto4,dto5
    };
// Get new vector with items where Name is Test and Note is asdasfa
    auto test = Where(numbers2, [](DTO dto) 
    {
        return dto.Name == "Test" &&
            dto.Note == "asdasfa";
    });
}

The goal is, to make it most comfortable, for the caller of the function.

Upvotes: 0

Views: 1123

Answers (3)

catnip
catnip

Reputation: 25388

You can write your template like this:

template<typename T>
std::vector<T> Where(std::vector<T> input, std::function <bool (T)> f)
{
    std::vector<T> v;
    for (auto it = input.begin(); it != input.end(); ++it)
        if (f(*it))
            v.push_back(*it);
    return v;
}

But then you must invoke it like this:

auto test = Where <DTO> (numbers2, [](DTO dto) 
{
    ...
});

Live demo


Edit: If you only ever want to pass a non-capturing lambda, it is slightly more efficient to define your template like this:

template<typename T>
std::vector<T> Where(std::vector<T> input, bool (* f) (T))
...

But you still need the 'extra machinery' at the call site that Lightness Races in Orbit refers to (I think template deduction fails because the number of template parameters no longer matches the number of parameters passed to Where).

Live demo

All-in-all, stick with your original code.

Upvotes: 1

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385264

In general, we try to stray from making templates necessitate a certain signature.

We prefer to:

  • document requirements, and
  • rely on compiler errors when something's not right.

As it is, your function template looks good, and your usage looks good, and that's why everything works.

Sure, you could pass something that returned not-bool that regardless worked in the if statement. And you could pass something that took not-DTO but worked anyway … but then so what? Just don't do those things. If it walks like a duck…

It's possible, with some machinery, to ensure that the callable takes specific arguments and returns a value of a specific type, but that would be complex and frankly not useful.

In short, your code is fine.

Upvotes: 2

cscrimge
cscrimge

Reputation: 395

Try changing the template signature of Where to SFINAE out the Func types you don't want:

template<typename T,
         typename Func,
         std::enable_if_t<std::is_same_v<bool, std::invoke_result_t<Func, T>>>* = nullptr>
vector<T> Where(vector<T> input, Func lambda)

Upvotes: 0

Related Questions