简体   繁体   中英

C++11 STL unique function, but in reverse

Is there a function which operates like std::unique , but that takes a custom comparison predicate, and retains the last element in an equivalent sequence, instead of the first? The answer is yes if C++14 or C++17 is an option; however I am using C++11.

I am starting with a deque of large heavy objects, sorted by one lightweight field. Some objects have equal values for the lightweight field, and this is unacceptable. I need to discard all but the last object in any sequence with a matching light field.

At present my code calls equal_range using a custom binary predicate and a helper heavy object, then rewinds the end iterator:

deque<Heavy> heavyDeque(...);

Light helperLight(...);
Heavy helperHeavy(helperLight, );

typedef deque<Heavy>:: iterator HevIt;

pair<HevIt, HevIt> deleteRange = equal_range(
    heavyDeque.begin(), heavyDeque.end(), helperHeavy,
    [](const Heavy & lhs, const Heavy & rhs) {
        return lhs.getLight() < rhs.getLight()})

//I have prior knowledge of at least one match
assert(deleteRange.first != deleteRange.second);
// back up; don't delete the last one
--(deleteRange.second);
heavyDeque.erase(deleteRange.first, deleteRange.second);

I am not too happy with having to pass in helperHeavy , when all I need from inside it is helperLight . I would rather my code look like this:

pair<HevIt, HevIt> deleteRange = magical_range_search(
    heavyDeque.begin(), heavyDeque.end(),
    [helperLight](const Heavy & heavy) {
        return helperLight == heavy.getLight()})

Note that my imaginary magical function takes a unary predicate, not a binary one.

something like this, perhaps?

template<typename BidirectionalIterator, typename UnaryPredicate>
pair<BidirectionalIterator,BidirectionalIterator>
magical_range_search(BidirectionalIterator begin,
                     BidirectionalIterator end, UnaryPredicate p)
{
  return {find_if(begin,end,p),
          find_if(make_reverse_iterator(end),make_reverse_iterator(begin),p)};
}

But, of course, for the whole thing you can just use std::unique with a reverse_iterator , as indicated in the comments:

heavyContainer.erase(heavyContainer.begin(),
                     unique(make_reverse_iterator(heavyContainer.end()),
                            make_reverse_iterator(heavyContainer.begin()),
                            [](Heavy const&lhs, Heavy const&rhs) {
                                return lhs.getLight() < rhs.getLight();
                            }).base());

Here reverse_iterator<>::base() returns the underlying original iterator of the reverse_iterator returned by unique() . As unique() returns the new end of the sequence, the reverse operation returns the new begin of the sequence. The new range is {new_begin, orignal_end} , and the elements in the range {original_begin, new_begin} must be erase() d.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM