Erasing items from an STL list

前端 未结 6 1848
北恋
北恋 2021-02-02 13:21

I want to make a function which moves items from one STL list to another if they match a certain condition.

This code is not the way to do it. The iterator will most lik

6条回答
  •  情话喂你
    2021-02-02 13:50

    Solution 1

    template
    Fwd move_if(Fwd first, Fwd last, Out result, Operation op)
    {
        Fwd swap_pos = first;
        for( ; first != last; ++first ) {
            if( !op(*first) ) *swap_pos++ = *first;
            else *result++ = *first;
        }
        return swap_pos;
    }
    

    The idea is simple. What you want to do is remove elements from one container and place them in another if a predicate is true. So take the code of the std::remove() algorithm, which already does the remove part, and adapt it to your extra needs. In the code above I added the else line to copy the element when the predicate is true.

    Notice that because we use the std::remove() code, the algorithm doesn't actually shrink the input container. It does return the updated end iterator of the input container though, so you can just use that and disregard the extra elements. Use the erase-remove idiom if you really want to shrink the input container.

    Solution 2

    template
    Bidi move_if(Bidi first, Bidi last, Out result, Operation op)
    {
        Bidi new_end = partition(first, last, not1(op));
        copy(new_end, last, result);
        return new_end;
    }
    

    The second approach uses the STL to implement the algorithm. I personally find it more readable than the first solution, but it has two drawbacks: First, it requires the more-powerful bidirectional iterators for the input container, rather than the forward iterators we used in the first solution. Second, and this is may or may not be an issue for you, the containers are not guaranteed to have the same ordering as before the call to std::partition(). If you wish to maintain the ordering, replace that call with a call to std::stable_partition(). std::stable_partition() might be slightly slower, but it has the same runtime complexity as std::partition().

    Either Way: Calling the Function

    list::iterator p = move_if(l1.begin(), l1.end(),
                                    back_inserter(l2),
                                    bind2nd(less(), 3));
    

    Final Remarks

    While writing the code I encountered a dilemma: what should the move_if() algorithm return? On the one hand the algorithm should return an iterator pointing to the new end position of the input container, so the caller can use the erase-remove idiom to shrink the container. But on the other hand the algorithm should return the position of the end of the result container, because otherwise it could be expensive for the caller to find it. In the first solution the result iterator points to this position when the algorithm ends, while in the second solution it is the iterator returned by std::copy() that points to this position. I could return a pair of iterators, but for the sake of making things simple I just return one of the iterators.

提交回复
热议问题