498. Requirements for partition() and stable_partition() too strong

Section: 28.7.4 [alg.partitions] Status: C++11 Submitter: Sean Parent, Joe Gottman Opened: 2005-05-04 Last modified: 2016-02-10

Priority: Not Prioritized

View all other issues in [alg.partitions].

View all issues with C++11 status.

Discussion:

Problem: The iterator requirements for partition() and stable_partition() [25.2.12] are listed as BidirectionalIterator, however, there are efficient algorithms for these functions that only require ForwardIterator that have been known since before the standard existed. The SGI implementation includes these (see http://www.sgi.com/tech/stl/partition.html and http://www.sgi.com/tech/stl/stable_partition.html).

[ 2009-04-30 Alisdair adds: ]

Now we have concepts this is easier to express!

Proposed resolution:

Add the following signature to:

Header <algorithm> synopsis [algorithms.syn]
p3 Partitions 28.7.4 [alg.partitions]

 template<ForwardIterator Iter, Predicate<auto, Iter::value_type> Pred>
   requires ShuffleIterator<Iter>
         && CopyConstructible<Pred>
   Iter partition(Iter first, Iter last, Pred pred);

Update p3 Partitions 28.7.4 [alg.partitions]:

Complexity: At most (last - first)/2 swaps. Exactly last - first applications of the predicate are done. If Iter satisfies BidirectionalIterator, at most (last - first)/2 swaps. Exactly last - first applications of the predicate are done.

If Iter merely satisfied ForwardIterator at most (last - first) swaps are done. Exactly (last - first) applications of the predicate are done.

[Editorial note: I looked for existing precedent in how we might call out distinct overloads overloads from a set of constrained templates, but there is not much existing practice to lean on. advance/distance were the only algorithms I could find, and that wording is no clearer.]

[ 2009-07 Frankfurt ]

Hinnant: if you want to partition your std::forward_list, you'll need partition() to accept ForwardIterators.

No objection to Ready.

Move to Ready.

Proposed resolution:

Change 25.2.12 from

template<class BidirectionalIterator, class Predicate> 
BidirectionalIterator partition(BidirectionalIterato r first, 
                                BidirectionalIterator last, 
                                Predicate pred); 

to

template<class ForwardIterator, class Predicate> 
ForwardIterator partition(ForwardIterator first, 
                          ForwardIterator last, 
                          Predicate pred); 

Change the complexity from

At most (last - first)/2 swaps are done. Exactly (last - first) applications of the predicate are done.

to

If ForwardIterator is a bidirectional_iterator, at most (last - first)/2 swaps are done; otherwise at most (last - first) swaps are done. Exactly (last - first) applications of the predicate are done.

Rationale:

Partition is a "foundation" algorithm useful in many contexts (like sorting as just one example) - my motivation for extending it to include forward iterators is foward_list - without this extension you can't partition an foward_list (without writing your own partition). Holes like this in the standard library weaken the argument for generic programming (ideally I'd be able to provide a library that would refine std::partition() to other concepts without fear of conflicting with other libraries doing the same - but that is a digression). I consider the fact that partition isn't defined to work for ForwardIterator a minor embarrassment.

[Mont Tremblant: Moved to Open, request motivation and use cases by next meeting. Sean provided further rationale by post-meeting mailing.]