On Mon, Mar 16, 2026 at 4:22 PM Jonathan Wakely <[email protected]> wrote:

> This implements LWG 4242 which was approved in Sofia 2025.
>
> I don't think the change from static_cast<const decay_t<I>&> to just
> static_cast<decay_t<I>> is observable, but it doesn't hurt. What fixes
> the problem identified in the issue is the is_array_v check, which
> avoids the static_cast entirely for volatile-qualified iterators.
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/ranges_base.h (distance(It&&, Sent)): Only decay
>         arrays to pointers when the type is actually an array, as per
>         LWG 4242.
>         * testsuite/24_iterators/range_operations/distance.cc: Add test
>         for LWG 4242.
> ---
>
> v2: No longer using auto(x), as that's not supported by Clang.
> Harmonized the _GLIBCXX_RESOLVE_LIB_DEFECTS comments and added the
> missing one for 3392. Better tests, and combined the new tests with the
> existing distance.cc file.
>
>  libstdc++-v3/include/bits/ranges_base.h        | 13 ++++++++++---
>  .../24_iterators/range_operations/distance.cc  | 18 ++++++++++++++++++
>  2 files changed, 28 insertions(+), 3 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/ranges_base.h
> b/libstdc++-v3/include/bits/ranges_base.h
> index 6cfa6fb6afe6..519927f5613e 100644
> --- a/libstdc++-v3/include/bits/ranges_base.h
> +++ b/libstdc++-v3/include/bits/ranges_base.h
> @@ -969,8 +969,6 @@ namespace ranges
>
>    struct __distance_fn final
>    {
> -    // _GLIBCXX_RESOLVE_LIB_DEFECTS
> -    // 3664. LWG 3392 broke std::ranges::distance(a, a+3)
>      template<typename _It, sentinel_for<_It> _Sent>
>        requires (!sized_sentinel_for<_Sent, _It>)
>        constexpr iter_difference_t<_It>
> @@ -985,11 +983,20 @@ namespace ranges
>         return __n;
>        }
>
> +    // _GLIBCXX_RESOLVE_LIB_DEFECTS
> +    // 3392. cannot be used on a move-only iterator with a sized sentinel
> +    // 3664. LWG 3392 broke std::ranges::distance(a, a+3)
>
This is indeed a better place to put it.
LGTM.

> +    // 4242. ranges::distance does not work with volatile iterators
>      template<typename _It, sized_sentinel_for<decay_t<_It>> _Sent>
>        [[nodiscard, __gnu__::__always_inline__]]
>        constexpr iter_difference_t<decay_t<_It>>
>        operator()(_It&& __first, _Sent __last) const
> -      { return __last - static_cast<const decay_t<_It>&>(__first); }
> +      {
> +       if constexpr (!is_array_v<remove_reference_t<_It>>)
> +         return __last - __first;
> +       else
> +         return __last - static_cast<decay_t<_It>>(__first);
> +      }
>
>      template<range _Range>
>        [[nodiscard, __gnu__::__always_inline__]]
> diff --git
> a/libstdc++-v3/testsuite/24_iterators/range_operations/distance.cc
> b/libstdc++-v3/testsuite/24_iterators/range_operations/distance.cc
> index 46e33a2aa701..65edeb8ab029 100644
> --- a/libstdc++-v3/testsuite/24_iterators/range_operations/distance.cc
> +++ b/libstdc++-v3/testsuite/24_iterators/range_operations/distance.cc
> @@ -154,6 +154,23 @@ test06()
>    VERIFY( std::ranges::distance(a+3, a) == -3 );
>  }
>
> +void
> +test_lwg4242()
> +{
> +  // LWG 4242. ranges::distance does not work with volatile iterators
> +  int arr[] = {1, 2, 3};
> +  int* volatile ptr = arr;
> +  auto d1 = std::distance(ptr, arr + 3);
> +  auto d2 = std::ranges::distance(ptr, arr + 3);
> +  VERIFY( d1 == d2 );
> +
> +  // This is not part of LWG 4242 but it doesn't hurt to check it anyway:
> +  volatile int vol_arr[1]{};
> +  auto d3 = std::distance(vol_arr, vol_arr + 1);
> +  auto d4 = std::ranges::distance(vol_arr, vol_arr + 1);
> +  VERIFY( d3 == d4 );
> +}
> +
>  int
>  main()
>  {
> @@ -163,4 +180,5 @@ main()
>    test04();
>    test05();
>    test06();
> +  test_lwg4242();
>  }
> --
> 2.53.0
>
>

Reply via email to