tags:

views:

124

answers:

5

I ran into a problem when calling std::partition on an empty container (std::list).

std::list<int>::iterator end_it = std::partition(l.begin(), l.end(), SomeFunctor(42));
std::list<int>::iterator it = l.begin();

while (it != end_it)
{
  // do stuff
}

If the list is empty, std::partition returns an iterator, that is not equal to l.end(). Is this the default behaviour?

+4  A: 

Am I missing something, or shouldn't:

std::list<int>::iterator end_it = l.begin();

be:

std::list<int>::iterator end_it = l.end();

But actually I think the return value of partition() is undefined for an empty set of values. The return value is defined to be:

An iterator i such that for any iterator j in the range [first, i), pred(j) != false, and for any iterator k in the range [i, last), pred(j) == false.

IMHO, the predicate cannot be applied to an end iterator, as it would not be dereferrencable.

anon
This does look like a bug in the OP code, but if the list is empty like he says then it doesn't matter anyway, so probably not the right answer.
John Zwinck
I think in that case the return value is defined. It should be equal to `first`. Which is equal to `last`. This iterator value satisfies the required condition: both halves are vacuously true, because both half-open ranges `[first,i)` and `[i,last)` are empty.
Steve Jessop
YUp: the reason is that there is the range [first, last) is empty. So a statement like "for any iterator j in the range [first, i) `pred(j)!= false` holds because there are no iterators j at all, and thus none for which `pred(j)==false`.
MSalters
A: 

This page shows the code that std::partition is behaving like.

unwind
A: 

No, std::partition should return the end iterator, and for me (gcc-4.4.2) it does.

I think you have a bug somewhere. Either in your code or in the compiler.

CAdaker
A: 

Stating the partition precondition that the range [first, last) should be a valid range, SGI says:

The range [i,j) is a valid range if both i and j are valid iterators, and j is reachable from i [2].

Which makes it ok to use partition on an empty range.

Of course, sgi is not the standard. But it's pretty close :)

xtofl
In this case maybe not. The Standard does not seem to specify a requirement or a precondition for partition(). Or I've missed it :-)
anon
There should be a statement somewhere that says the iterator arguments to `partition()` represent a range. If not, a DR would be in order.
MSalters
+2  A: 

Right now, there really is no good answer. The Library Working Group of the C++ committee issue number 1205 covers exactly this question. That issue includes both a primary and an alternative proposed resolution but neither has been accepted or rejected yet (and I don't like either one).

Since the standard doesn't give an unambiguous definition of the result of applying an algorithm to an empty range, I'd say the result of doing so is currently undefined. I think there's some hope that it'll be defined in the next version of the standard, but for now it's really not. Even when it is, from a practical viewpoint it'll probably be better to avoid it at least for a while, because it may be a while before compilers conform in this respect (though it should usually be a fairly easy fix).

Edit, mostly in response to Martin B's comment: the issue is listed for [alg.partitions], which includes both std::partition and std::stable_partition. Unfortunately, the proposed wording does not seem to directly address either one. The paragraph it cites is (at least in N2960) in the description of std::is_paritioned, which is pretty much what Martin B described, even though he used the wrong name for it. Worse, the primary proposed resolution is in the form of non-normative notes.

As I said, though, I don't really like either proposed resolution. The primary tries to place requirements in non-normative notes. Such notes are fine if they clarify requirements that are really already present elsewhere, but might be hard to find. In this case, I'm pretty sure the requirements really aren't present. The alternative resolution is better, but fails to address the core question of whether an empty range is a valid range.

IMO, a better resolution would start at §24.1/7. This already tells us that: "A range [i, i) is an empty range;..." I think it should add normative language to explicitly state that an empty range either is or is not a valid range. If it's not a valid range, nothing else needs to be added -- it's already explicit that applying an algorithm to an invalid range gives undefined behavior.

If an empty range is valid, then normative wording needs to be added to define the results for applying each algorithm to an empty range. This would answer the basic question, then state what that answer implies for each specific algorithm.

Jerry Coffin
A link to issue 1205 is here: http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#1205 The only reference to partitions that I find here is std::partitions() (note the S at the end), which, I presume, tests whether a particular sequence is partitioned by a value or not. Have you got a link that specifically references std::partition()?
Martin B
+1 Wow... thanks for the comprehensive update!
Martin B