WG21-SG14/SG14

sg14::ring_span not compatible with range-v3 (not a semiregular type)

seertaak opened this issue · 5 comments

ring_span is not a semiregular type, because it (and its iterator) lack default constructors. There may be other operations missing. Assuming addition of these does not negatively impact performance, it would be nice for both libraries to interoperate without friction.

Default-constructible iterators makes sense to me. Default-constructible ring_span I think makes sense, but could you clarify: what should be the state of a default-constructed ring_span? Would we have to ensure that it was in some sense "empty", or would it be okay to be in a "partially constructed / moved-from" state?

(The answers would presumably be similar to whatever string_view does. I understand that there is an ongoing debate about the validity of string_view{nullptr}, but I believe that string_view{} is already legal in C++17 and does... something. :)

...Although, to be clear, I personally would be fine with sticking to our philosophical guns and saying that rings and iterators do not have partially-formed states, and if you want a "maybe-ring" type, you should use optional<ring_span>. I know that's not how C++ does things though.

Sorry for delay in replying. Default-constructable iterators/span should be allowed, and as you suggest should leave the object in a partially-formed state where only valid operation is assignment to properly-initialized span/iterator. It would be "crazy" to do a check :)

I must say: I'm not really an expert in this stuff.

The Ranges TS does not require default-constructed ranges to be in the domain of any range operations (begin, end, empty, size, etc.), so an implementation that only supports destruction/assignment/swap is perfectly reasonable.

There are some requirements on value-initialized forward iterators that we "inherited" from the IS: value-initialized iterators should behave is if they denote the same empty range. Given a value-initialized forward iterator i, that means that i == i, !(i != i) must hold. For a value-initialized random access iterator, equality generalizes to cover distance and ordering so i - i == 0, !(i < i), i <= i, !(i > i), and i >= i must all hold as well.

The TS requires that Views are Semiregular (and therefore DefaultConstructible), but there are no required construction/destruction operations at all for general Ranges. If you want ring_span to model View and participate in View composition, it will need to be Semiregular with amortized O(1) copy/move construction/assignment and default construction. If you only care that it models Range, you can ignore much of this paragraph ;)

Default-constructible iterators makes sense to me. Default-constructible ring_span I think makes sense, but could you clarify: what should be the state of a default-constructed ring_span? Would we have to ensure that it was in some sense "empty", or would it be okay to be in a "partially constructed / moved-from" state?

What is the motivation for empty() to return false when ring_span is in the "moved-from" state ? If that were allowed, empty() would need a precondition on not being called when the object is in the moved-from state, and all methods with preconditions on empty() (EDIT: and size, and similar methods) would need preconditions on the moved-from state as well.

Would we have to ensure that it was in some sense "empty", or would it be okay to be in a "partially constructed / moved-from" state?
What is the motivation for empty() to return false when ring_span is in the "moved-from" state?

Not necessarily "false"; more like what you said next: there could be a precondition that you're not allowed to call .empty() on a moved-from object. The rationale is that empty() might be implemented as size()==0, and size() might be implemented as end() - begin(), and calling either begin() or end() on a moved-from object might access through a null pointer.

Casey's answer implies that this is okay as far as the Concepts are concerned:

The Ranges TS does not require default-constructed ranges to be in the domain of any range operations (begin, end, empty, size, etc.)

And yes, this is super confusing and ugly, thanks for asking. "I personally would be fine with sticking to our philosophical guns and saying that rings and iterators do not have partially-formed states, and if you want a "maybe-ring" type, you should use optional<ring_span>." 🙂

Anyway, pull requests welcome AFAIC and I'll happily merge whatever looks good to people; I'm unlikely to initiate any patches on my own.