Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Negate a lambda without knowing the argument type?

I'm trying to write an in-place filter function that works similarly to Python's filter. For example:

std::vector<int> x = {1, 2, 3, 4, 5};
filter_ip(x, [](const int& i) { return i >= 3; });
// x is now {3, 4, 5}

First I tried this:

template <typename Container, typename Filter>
void filter_ip(Container& c, Filter&& f)
{
  c.erase(std::remove_if(c.begin(), c.end(), std::not1(f)), c.end());
}

However, that doesn't work because lambdas don't have an argument_type field.

This following variant does work:

template <typename Container, typename Filter>
void filter_ip(Container& c, Filter&& f)
{
  c.erase(std::remove_if(c.begin(), c.end(), 
                         [&f](const typename Container::value_type& x) { 
                            return !f(x); 
                         }), 
          c.end());
}

However, it seems less than ideal because before, it would only have required that Container have begin, end, and erase, while now it also requires that it defines a value_type. Plus it looks a little unwieldy.

This is the 2nd approach in this answer. The first would use std::not1(std::function<bool(const typename Container::value_type&)>(f)) instead of the lambda, which still requires the type.

I also tried specifying the arg func as an std::function with a known argument type:

template <typename Container, typename Arg>
void filter_ip(Container& c, std::function<bool(const Arg&)>&& f)
{
  c.erase(std::remove_if(c.begin(), c.end(), std::not1(f)), c.end());
}

But then I get:

'main()::<lambda(const int&)>' is not derived from 'std::function<bool(const Arg&)>'

Is there any way around this? Intuitively it seems it should be really simple since all you need to do is apply a not to a bool which you already know f returns.

like image 558
Claudiu Avatar asked Sep 20 '26 03:09

Claudiu


1 Answers

If you can't use C++14 generic lambdas, how about delegating to a classic functor with a templated operator() :

#include <utility>
#include <vector>
#include <algorithm>
#include <iostream>

template <class F>
struct negate {
    negate(F&& f)
    : _f(std::forward<F>(f)) {}

    template <class... Args>
    bool operator () (Args &&... args) {
        return !_f(std::forward<Args>(args)...);
    }

private:
    F _f;
};

template <typename Container, typename Filter>
void filter_ip(Container& c, Filter&& f)
{
    c.erase(std::remove_if(
        c.begin(),
        c.end(),
        negate<Filter>(std::forward<Filter>(f))),
        c.end()
    );
}

int main() {
    std::vector<int> v {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    filter_ip(v, [](int i) {return bool(i%2);});
    for(auto &&i : v)
        std::cout << i << ' ';
    std::cout << '\n';
}

Output :

1 3 5 7 9 

Live on Coliru

like image 171
Quentin Avatar answered Sep 22 '26 16:09

Quentin