@@ -2263,115 +2263,162 @@ inline auto make_args(int argc, char** argv) -> args
2263
2263
//
2264
2264
// range: a range of [begin, end) or [first, last]
2265
2265
//
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
+ //
2266
2276
// -----------------------------------------------------------------------
2267
2277
//
2268
2278
template <typename T>
2269
- struct range
2279
+ class range
2270
2280
{
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:
2271
2295
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&;
2275
2299
2276
2300
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
2280
2304
)
2305
+ // For smaller-than-size_t/ptrdiff_t numeric types, these will widen
2281
2306
: first{ f }
2282
2307
, last{ l }
2283
2308
{
2309
+ // Represent all ranges as half-open; after this we can forget the flag
2284
2310
if (include_last) {
2285
2311
++last;
2286
2312
}
2287
2313
}
2288
2314
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
-
2300
2315
class iterator
2301
2316
{
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
+
2302
2331
public:
2303
2332
using difference_type = std::ptrdiff_t ;
2304
2333
using value_type = T;
2305
2334
using pointer = T*;
2306
2335
using reference = T&;
2307
- using iterator_category = std::random_access_iterator_tag ;
2336
+ using iterator_category = typename range_iterator_category<T>::tag ;
2308
2337
2309
2338
iterator () { }
2310
2339
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 } {}
2312
2341
2313
2342
auto operator <=>(iterator const &) const = default ;
2314
2343
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
+ }
2318
2360
}
2319
2361
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; }
2324
2366
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)
2326
2369
//
2327
2370
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
+ }
2330
2382
}
2331
2383
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 ; }
2334
2388
2335
2389
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; }
2337
2392
2338
2393
auto operator + (difference_type i ) const -> iterator { auto ret = *this ; return ret += i; }
2339
2394
auto operator - (difference_type i ) const -> iterator { auto ret = *this ; return ret -= i; }
2340
2395
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{};
2352
2396
};
2353
2397
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; }
2360
2404
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
+ }
2364
2418
}
2365
-
2366
- T first;
2367
- T last;
2368
2419
};
2369
2420
2370
2421
2371
- template <typename T, typename U>
2372
- range (T, U, bool = false ) -> range<std::common_type_t<T, U>>;
2373
-
2374
-
2375
2422
// -----------------------------------------------------------------------
2376
2423
//
2377
2424
// alien_memory: memory typed as T but that is outside C++ and that the
0 commit comments