
<snip>
With such a notion this, indeed, is a rather useless component. I have to agree with Tomas and others in one of their points: one of the major advantages or ranges before pairs of iterators is that they attempt to reproduce containers as far as possible. Yes, in general default-constructed iterators are singular and they cannot be used reliably. However, not all iterators behave that way. Pointers, for example, are NULL on default-initialization (which is used in the default constructor of the iterator_range class). I can well have my own iterators that are default-constructible and allow comparison in this state. And I don't understand why an iterator range of these iterators suddenly restricts me from being able to compare these iterators in empty(), for example.
The documentation of the iterator_range states for several functions, such as size() and operator[], that these functions depend on the iterator nature. I believe, empty() could just do the same - rely on the ability to compare iterators, even default-initialized ones. Of course, this should be clearly stated in the docs.
Whilst the documentation might say that, the implementation of size() and operator[] depends on underlying iterator nature, the reality is that both these functions will assert on a singular range.
If it was just me, default-constructing iterators and ranges shouldn't even
be allowed (unless that is sufficient that initialize it). A range that is not properly initialized is not in any usable state. It's not an empty range, it's more like a pointer to somewhere random. Just don't try to use it. If Boost.Range used to allow it, that was a bug; good thing it is now fixed.
As for is_singular, that function shouldn't be exposed to the public.
I agree that is_singular is evil. More over, I am sure that asserts based on it are also evil. My point is: don't try to make iterator_range more clever than it is stated in the docs (that is, a pair of iterators with a forwarding interface of a container).
This is the crux of the issue. What is an iterator_range trying to be. If it is supposed to be nothing more than a glorified pair of iterators, then so be it. Allow singular ranges, but don't assert on them. If on the other hand it wants to make a pair of iterators look like a container, then there pretty much has to be a way to create an empty range without having to tie that empty range to a specific container. The glorified pair will only ever get used as a glorified pair. A pair of iterators looking like a container though is far more useful in generic programming - so that would be my personal proposal. At the moment, iterator_range is trying to be both things, and failing at both as well. Dave