Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to emulate remove_unless

I have code to remove all elements from a std::vector<int> that are less than some int limit. I've written some functions that partially apply lambdas:

auto less_than_limit = [](int limit) {
  return [=](int elem) {
    return limit > elem;
  };
};

auto less_than_three = less_than_limit(3);

When I test it with std::vector<int> v{1,2,3,4,5};, I get the expected results:

for(auto e: v) {
  std::cout << less_than_three(e) << " ";
}
// 1 1 0 0 0

I can easily remove all the elements less than three:

auto remove_less_than_three = std::remove_if(std::begin(v), std::end(v), less_than_three);

v.erase(remove_less_than_three, v.end());

for(auto e: v) {
  std::cout << e << " ";
}
// 3 4 5

How would I remove elements greater than or equal to 3 using less_than_three?

I tried wrapping less_than_three in std::not1, but got errors:

/usr/local/Cellar/gcc/5.3.0/include/c++/5.3.0/bits/stl_function.h:742:11: error: no type named 'argument_type' in 'struct main()::<lambda(int)>::<lambda(int)>'
     class unary_negate
           ^
/usr/local/Cellar/gcc/5.3.0/include/c++/5.3.0/bits/stl_function.h:755:7: error: no type named 'argument_type' in 'struct main()::<lambda(int)>::<lambda(int)>'
       operator()(const typename _Predicate::argument_type& __x) const

/usr/local/Cellar/gcc/5.3.0/include/c++/5.3.0/bits/predefined_ops.h:234:30: error: no match for call to '(std::unary_negate<main()::<lambda(int)>::<lambda(int)> >) (int&)'
  { return bool(_M_pred(*__it)); }
                              ^

I then tried std::not1(std::ref(less_than_three)), but got these errors:

/usr/local/Cellar/gcc/5.3.0/include/c++/5.3.0/bits/stl_function.h:742:11: error: no type named 'argument_type' in 'class std::reference_wrapper<main()::<lambda(int)>::<lambda(int)> >'
     class unary_negate
           ^
/usr/local/Cellar/gcc/5.3.0/include/c++/5.3.0/bits/stl_function.h:755:7: error: no type named 'argument_type' in 'class std::reference_wrapper<main()::<lambda(int)>::<lambda(int)> >'
       operator()(const typename _Predicate::argument_type& __x) const
       ^
/usr/local/Cellar/gcc/5.3.0/include/c++/5.3.0/bits/predefined_ops.h:234:30: error: no match for call to '(std::unary_negate<std::reference_wrapper<main()::<lambda(int)>::<lambda(int)> > >) (int&)'
  { return bool(_M_pred(*__it)); }
                              ^

How can I negate the function in std::remove_if without changing the logic of my lambdas? In other words, how can I emulate remove_unless?

like image 467
erip Avatar asked Jan 12 '16 17:01

erip


4 Answers

not1 is somewhat obsolete (and requires that the functor provides certain member typedefs, which a lambda clearly doesn't).

You'll have to write a negator yourself:

auto negate = [] (auto&& f) {return [f=std::forward<decltype(f)>(f)] 
            (auto&&... args) {return !f(std::forward<decltype(args)>(args)...);};};

Demo.

like image 83
Columbo Avatar answered Nov 02 '22 19:11

Columbo


std::not1 presumes your function object will derive from std::unary_function, or at least provide the same interface, so it'll have typedefs for result_type and argument_type.

Since a lambda won't define those, you won't be able to use not1 on them.

The obvious choice would be to create something similar to not1 yourself, but using more modern techniques to detect/pass through the argument/result type from whatever it modifies.

If you really want to use not1, then the most sensible approach would be to do the comparison with std::less and std::bind to specify the value to which you're going to compare (i.e., it's basically a C++03 thing, so if you're going to use it, you write everything in the style of C++03).

like image 7
Jerry Coffin Avatar answered Nov 02 '22 19:11

Jerry Coffin


You can also define the return type of the lambda with std::function.

auto remove_gte_three = std::remove_if(std::begin(v), std::end(v), std::not1(std::function<int(int)>(less_than_three)));
like image 4
George Houpis Avatar answered Nov 02 '22 18:11

George Houpis


Old way with not1() negator (C++11):

// You apply the not1() negator adapter 
// to the result of  less_than_three() like this:
std::function<bool(int)> f = less_than_three;
auto it = remove_if(begin(v), end(v), not1(f));

New way with lambda (C++14):

// Or with lambda you can negate an unary predicate.
// Thanks to Stephan T. Lavavej
template <typename T, typename Predicate>
void keep_if(std::vector<T>& v, Predicate pred)
{
    auto notpred = [&pred](const T& t) { return !pred(t); };
    v.erase(remove_if(v.begin(), v.end(), notpred), v.end());
}

Usage:

keep_if(v, less_than_three);

Or more general solution (C++14):

template <ForwardIterator I, Predicate P>
I remove_if_not(I first, I last, P pred)
{
    return std::remove_if(first, last,
            [&](const ValueType(I)& x){ return !pred(x); });
}

Usage:

auto p = remove_if_not(begin(v), end(v), less_than_three);
v.erase(p, v.end());
// Result: 1 2
like image 1
Marko Tunjic Avatar answered Nov 02 '22 18:11

Marko Tunjic