10

I've seen a few places on the internet where they describe using std::copy_if with std::make_move_iterator, but if the iterator were to be a forward iterator, that would result in having valid but unspecified (VBU) objects scattered around the source container.

Wouldn't it be better to have a std::move_if algorithm such that if an object is moved, then it would move the resulting VBU object to the end of the range, like that which is done in the std::remove_if algorithm, consolidating all of the VBU objects together so that they can be erased or reassigned?

Adrian
  • 10,246
  • 4
  • 44
  • 110

1 Answers1

18

If move_if existed as an algorithm, it would have to be specified as:

template <class InputIt, class OutputIt, class UnaryPredicate>
OutputIt move_if(InputIt first, InputIt last, OutputIt d_first, UnaryPredicate pred)
{
    return std::copy_if(std::make_move_iterator(first), std::make_move_iterator(last),
        d_first, pred);
}

We are very used to thinking of the difference between copying and moving as simply a matter of whether or not we care about the source object. We still do? Copy. We don't? Move. Having move_if(f, l, d, pred) do something semantically different than copy_if(f, l, d, pred) seems inherently confusing and error-prone - since the inevitable usage of it would be to do a "copy where we don't care about the source anymore."

But then - you're more or less describing the problems with this algorithm in your question. When would I use this? I would end up with a source range where some of the elements are moved-from but others aren't. What could I do with such a range? I couldn't coalesce them somehow since I don't know which ones they are or anything. Moving those elements to the back is useful - and we do have that algorithm: remove_if.

Basically the only thing I could do with the source range at this point is destroy it. Maybe that's useful. Maybe that's useful enough to even merit this algorithm in std::. I don't know. But move_if definitely needs to do the same thing as copy_if.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • 1
    Hmmm, that's reasonable. Now that I think about it, this can be accomplished with `std::partition` and `std::move` algorithms. – Adrian Apr 03 '19 at 19:47
  • 2
    Another caveat of `move_if` as implemented in your answer is that the predicate needs to take its argument by reference, otherwise, the value to be potentially moved is moved into the predicate argument instead of the output range. – Ton van den Heuvel Mar 17 '20 at 09:58
  • The move is implicit by the use of `std::make_move_iterator`. So the following for example already has issues, suppose `v` and `w` are both of type `std::vector`: `move_if(v.begin(), v.end(), std::back_inserter(w), [](std::string s) { return true; })`. Now, both `v` and `w` will contain empty strings. The character data itself is lost. I agree that the predicate should take its argument by const reference, but breaking the code by not having a const reference there is at least surprising. – Ton van den Heuvel Mar 17 '20 at 16:06