@@ -2263,115 +2263,162 @@ inline auto make_args(int argc, char** argv) -> args
22632263//
22642264// range: a range of [begin, end) or [first, last]
22652265//
2266+ // TT is the type we actually store for 'first' and 'last'.
2267+ //
2268+ // If T is integral, store a widened representation to ensure that
2269+ // the past-the-end value is representable even if [first,last] are
2270+ // numeric_limits<T> [min,max].
2271+ //
2272+ // This lets us represent all ranges as half-open ranges using just
2273+ // 'first' and (possibly-adjusted-by-one) 'last' without any extra
2274+ // data or a Closed parameter etc. = single simpler implementation.
2275+ //
22662276// -----------------------------------------------------------------------
22672277//
22682278template <typename T>
2269- struct range
2279+ class range
22702280{
2281+ using TT = std::conditional_t <
2282+ std::is_integral_v<T>,
2283+ std::conditional_t <
2284+ std::is_signed_v<T>,
2285+ std::ptrdiff_t ,
2286+ std::size_t
2287+ >,
2288+ T
2289+ >;
2290+
2291+ TT first;
2292+ TT last;
2293+
2294+ public:
22712295 using difference_type = std::ptrdiff_t ;
2272- using value_type = T;
2273- using pointer = T*;
2274- using reference = T&;
2296+ using value_type = T;
2297+ using pointer = T*;
2298+ using reference = T&;
22752299
22762300 range (
2277- T const & f,
2278- T const & l,
2279- bool include_last = false
2301+ T const & f,
2302+ std:: type_identity_t <T> const & l,
2303+ bool include_last = false
22802304 )
2305+ // For smaller-than-size_t/ptrdiff_t numeric types, these will widen
22812306 : first{ f }
22822307 , last{ l }
22832308 {
2309+ // Represent all ranges as half-open; after this we can forget the flag
22842310 if (include_last) {
22852311 ++last;
22862312 }
22872313 }
22882314
2289- // If T is numeric, use explicit narrowing to avoid compiler warnings
2290- static auto inc_by (T& t, difference_type i) -> T&
2291- {
2292- if constexpr (std::integral<T>) {
2293- return t += unsafe_narrow<T>(i);
2294- }
2295- else {
2296- return t += i;
2297- }
2298- }
2299-
23002315 class iterator
23012316 {
2317+ TT first = T{};
2318+ TT last = T{};
2319+ TT curr = T{};
2320+
2321+ // Helper type trait to check for the existence of iterator_category
2322+ template <typename I, typename = void >
2323+ struct range_iterator_category {
2324+ using tag = std::random_access_iterator_tag;
2325+ };
2326+ template <typename I>
2327+ struct range_iterator_category <I, std::void_t <typename std::iterator_traits<I>::iterator_category>> {
2328+ using tag = typename std::iterator_traits<I>::iterator_category;
2329+ };
2330+
23022331 public:
23032332 using difference_type = std::ptrdiff_t ;
23042333 using value_type = T;
23052334 using pointer = T*;
23062335 using reference = T&;
2307- using iterator_category = std::random_access_iterator_tag ;
2336+ using iterator_category = typename range_iterator_category<T>::tag ;
23082337
23092338 iterator () { }
23102339
2311- iterator (T const & f, T const & l, T start) : first{ f }, last{ l }, curr{ start } {}
2340+ iterator (TT const & f, TT const & l, TT start) : first{ f }, last{ l }, curr{ start } {}
23122341
23132342 auto operator <=>(iterator const &) const = default ;
23142343
2315- auto operator *() const -> T {
2316- if (curr != last) { return curr; }
2317- else { return T{}; }
2344+ // In this section, we don't use relational comparisons so that
2345+ // this works when T is a less-powerful-than-random-access iterator
2346+ //
2347+ auto operator *() const -> T
2348+ {
2349+ if (curr != last) {
2350+ if constexpr (std::is_same_v<T, TT>) {
2351+ return curr;
2352+ }
2353+ else {
2354+ return unsafe_narrow<T>(curr);
2355+ }
2356+ }
2357+ else {
2358+ return T{};
2359+ }
23182360 }
23192361
2320- auto operator ++() -> iterator& { if (curr != last ) { ++curr; } return *this ; }
2321- auto operator --() -> iterator& { if (curr != first) { --curr; } return *this ; }
2322- auto operator ++(int ) -> iterator { auto old = *this ; ++*this ; return old; }
2323- auto operator --(int ) -> iterator { auto old = *this ; ++*this ; return old; }
2362+ auto operator ++() -> iterator& { if (curr != last ) { ++curr; } return *this ; }
2363+ auto operator --() -> iterator& { if (curr != first) { --curr; } return *this ; }
2364+ auto operator ++(int ) -> iterator { auto old = *this ; ++*this ; return old; }
2365+ auto operator --(int ) -> iterator { auto old = *this ; ++*this ; return old; }
23242366
2325- // And now all the random-access operations (valid if T is random-access)
2367+ // And now all the random-access operations which can use relational
2368+ // comparisons (these functions are valid if T is random-access)
23262369 //
23272370 auto operator [](difference_type i) const -> T {
2328- if (curr + i != last) { return curr + i; }
2329- else { return T{}; }
2371+ if (curr + i != last) {
2372+ if constexpr (std::is_same_v<T, TT>) {
2373+ return curr + i;
2374+ }
2375+ else {
2376+ return unsafe_narrow<T>(curr + i);
2377+ }
2378+ }
2379+ else {
2380+ return T{};
2381+ }
23302382 }
23312383
2332- auto operator +=(difference_type i) -> iterator& { if (curr + i <= last ) { inc_by (curr, i); } else { curr = last; } return *this ; }
2333- auto operator -=(difference_type i) -> iterator& { if (curr - i >= first) { inc_by (curr, -i); } else { curr = first; } return *this ; }
2384+ auto operator +=(difference_type i) -> iterator&
2385+ { if (curr + i <= last ) { curr += i; } else { curr = last; } return *this ; }
2386+ auto operator -=(difference_type i) -> iterator&
2387+ { if (curr - i >= first) { curr -= i; } else { curr = first; } return *this ; }
23342388
23352389 friend
2336- auto operator + (difference_type i, iterator const & this_) -> iterator { auto ret = *this_; return ret += i; }
2390+ auto operator + (difference_type i, iterator const & iter) -> iterator
2391+ { auto ret = *iter; return ret += i; }
23372392
23382393 auto operator + (difference_type i ) const -> iterator { auto ret = *this ; return ret += i; }
23392394 auto operator - (difference_type i ) const -> iterator { auto ret = *this ; return ret -= i; }
23402395 auto operator - (iterator that) const -> difference_type { return that.curr - curr; }
2341-
2342- // auto operator+(difference_type i) -> iterator {
2343- // if (i > 0) { return { first, last, std::min(curr + i, last) }; }
2344- // else { return { first, last, std::max(curr + i, 0) }; }
2345- // }
2346- // auto operator- (difference_type i) -> iterator { return operator+(-i); }
2347-
2348- private:
2349- T first = T{};
2350- T last = T{};
2351- T curr = T{};
23522396 };
23532397
2354- auto begin () const -> iterator { return iterator{ first, last, first }; }
2355- auto end () const -> iterator { return iterator{ first, last, last }; }
2356- auto cbegin () const -> iterator { return begin (); }
2357- auto cend () const -> iterator { return end (); }
2358- auto size () const -> std::size_t { return unsafe_narrow<std::size_t >(ssize ()); }
2359- auto ssize () const -> int { return last - first; }
2398+ auto begin () const -> iterator { return iterator{ first, last, first }; }
2399+ auto end () const -> iterator { return iterator{ first, last, last }; }
2400+ auto cbegin () const -> iterator { return begin (); }
2401+ auto cend () const -> iterator { return end (); }
2402+ auto size () const -> std::size_t { return unsafe_narrow<std::size_t >(ssize ()); }
2403+ auto ssize () const -> std::ptrdiff_t { return last - first; }
23602404
2361- auto operator [](difference_type i) const {
2362- if (0 <= i && i < ssize ()) { return first + i; }
2363- else { return T{}; }
2405+ auto operator [](difference_type i) const -> T
2406+ {
2407+ if (0 <= i && i < ssize ()) {
2408+ if constexpr (std::is_same_v<T, TT>) {
2409+ return first + i;
2410+ }
2411+ else {
2412+ return unsafe_narrow<T>(first + i);
2413+ }
2414+ }
2415+ else {
2416+ return T{};
2417+ }
23642418 }
2365-
2366- T first;
2367- T last;
23682419};
23692420
23702421
2371- template <typename T, typename U>
2372- range (T, U, bool = false ) -> range<std::common_type_t<T, U>>;
2373-
2374-
23752422// -----------------------------------------------------------------------
23762423//
23772424// alien_memory: memory typed as T but that is outside C++ and that the
0 commit comments