
My point was that a default constructed range may be in a perfectly valid
state, too, depending on its iterators.
Indeed. I think that a default constructed iterator_range should be usable and empty iff default constructed iterators are non-singular and equal. This effectively pushes dealing with default construction onto the iterators. IMO, this is more consistent than the old behavior, where a default constructed iterator_range was "empty", but r.begin() == r.end() could be undefined behavior.
I think this is the right answer (and pretty well argued by now). The behavior of the iterator_range should "inherit" (is there a better word for this?) its semantics from the underlying iterator. To do otherwise would impose requirements on iterators that may not (easily) support those semantics, making the concept less generic (as in represents fewer possible implementations). Considering the converse, you'd have to provide some kind of axiomatic guarantee at the concept level. Ranges that are empty by default might be a refinement of this (to borrow DA's name) NonSingularRange that does provide this guarantee. concept Range<typename X> { ... } concept NonSingularRange<typename X> : Range<X> { axiom EmptyByDefault() { X() == X() && empty(X()); } } If you publish your interfaces with these constraints (using Boost.ConceptCheck, for example), then the burden of ensuring proper initialization lies with your users, not you. As for fixing the process... we should probably start getting in the habit of trying to actually write the concept definitions even though we can't compile them. At least it would seem to provide an unambiguous statement of semantic behavior. Plus, it's good practice. It also lets us build tests to a definition rather than the documentation. Just a thought. Andrew Sutton andrew.n.sutton@gmail.com