Move all elements which satisfy some condition from one container to another, i.e. I'm looking for some kind of “move_if”

前端 未结 3 1120
有刺的猬
有刺的猬 2021-01-04 00:13

Given

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

I want to move all elements e which satisfy some co

相关标签:
3条回答
  • 2021-01-04 00:53

    @T.C. has provided a perfectly working solution. However, at a first glance, one may not understand what the intend of that code is. So, it might be not perfect, but I tend to 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;
    }
    
    0 讨论(0)
  • 2021-01-04 00:58

    The reason why move_if doesn't exist is because it would bloat the library. Either use copy_if with move iterator or write it yourself.

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

    Here is an implementation by Jonas_No found at 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;
    }
    
    0 讨论(0)
  • 2021-01-04 01:00

    If the moved-from elements can stay where they are in first, then 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 the moved-from elements should be erased from first, I'd 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 a move_iterator, followed by 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);
    
    0 讨论(0)
提交回复
热议问题