Move all elements that satisfy a certain condition from one container to another, i.e. I'm looking for some kind of "move_if",

Considering

std::vector<T> first = /* some given data */, second; 

I want to move all elements of e that satisfy some cond(e) condition from first to second , i.e. something like

 move_if(std::make_move_iterator(first.begin()), std::make_move_iterator(first.end()), std::back_inserter(second), [&](T const& e) { return cond(e); }); 

I could not install this using the algorithm library. So how can I do this?

+7
c ++ c ++ 11 c ++ 14
source share
3 answers

If the moved items can stay where they are in first , just use copy_if with move_iterator .

 std::copy_if(std::make_move_iterator(first.begin()), std::make_move_iterator(first.end()), std::back_inserter(second), cond); 

If moved items should be removed from first , I would do

 // partition: all elements that should not be moved come before // (note that the lambda negates cond) all elements that should be moved. // stable_partition maintains relative order in each group auto p = std::stable_partition(first.begin(), first.end(), [&](const auto& x) { return !cond(x); }); // range insert with move second.insert(second.end(), std::make_move_iterator(p), std::make_move_iterator(first.end())); // erase the moved-from elements. first.erase(p, first.end()); 

Or partition_copy with move_iterator followed by an assignment:

 std::vector<T> new_first; std::partition_copy(std::make_move_iterator(first.begin()), std::make_move_iterator(first.end()), std::back_inserter(second), std::back_inserter(new_first), cond); first = std::move(new_first); 
+22
source share

The reason move_if does not exist is because it inflates the library. Either use copy_if with moving the iterator, or write it yourself.

 copy_if(move_iterator<I>(f), move_iterator<I>(l), out); 

Here is the Jonas_No implementation found on channel9.

 template <typename FwdIt, typename Container, typename Predicate> inline FwdIt move_if(FwdIt first, FwdIt last, Container &cont, Predicate pred) { if (first == last) return last; // Empty so nothing to move const size_t size = count_if(first, last, pred); if (size == 0) return last; // Nothing to move cont.resize(size); FwdIt new_end = first; auto c = cont.begin(); for (auto i = first; i != last; ++i) { if (pred(*i)) // Should it move it ? *c++ = move(*i); else *new_end++ = move(*i); } return new_end; } 
+1
source share

@TC provided a perfectly working solution. However, at first glance, you cannot understand what the intent of this code is. So this may not be perfect, but I prefer something like this:

 template<class InputIt, class OutputIt, class InputContainer, class UnaryPredicate> OutputIt move_and_erase_if(InputIt first, InputIt last, InputContainer& c, OutputIt d_first, UnaryPredicate pred) { auto dist = std::distance(first, last); while (first != last) { if (pred(*first)) { *d_first++ = std::move(*first); first = c.erase(first); last = std::next(first, --dist); } else { ++first; --dist; } } return d_first; } 
+1
source share

All Articles