[libcxx-commits] [libcxx] [libc++] Optimize {std, ranges}::distance for segmented iterators (PR #133612)
Peng Liu via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Jun 27 15:09:51 PDT 2025
================
@@ -10,41 +10,82 @@
#ifndef _LIBCPP___ITERATOR_DISTANCE_H
#define _LIBCPP___ITERATOR_DISTANCE_H
+#include <__algorithm/for_each_segment.h>
#include <__config>
#include <__iterator/concepts.h>
#include <__iterator/incrementable_traits.h>
#include <__iterator/iterator_traits.h>
+#include <__iterator/segmented_iterator.h>
#include <__ranges/access.h>
#include <__ranges/concepts.h>
#include <__ranges/size.h>
#include <__type_traits/decay.h>
+#include <__type_traits/enable_if.h>
#include <__type_traits/remove_cvref.h>
+#include <__utility/move.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
_LIBCPP_BEGIN_NAMESPACE_STD
-template <class _InputIter>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 typename iterator_traits<_InputIter>::difference_type
-__distance(_InputIter __first, _InputIter __last, input_iterator_tag) {
- typename iterator_traits<_InputIter>::difference_type __r(0);
+#if _LIBCPP_STD_VER >= 20
+template <class _Iter>
+using __iter_distance_t _LIBCPP_NODEBUG = std::iter_difference_t<_Iter>;
----------------
winner245 wrote:
> I am now consistently using `iterator_traits<I>::difference_type` as the return type for the non-range `std::__distance` algorithm.
However, this approach fails when the range algorithm `std::ranges::distance` forwards to `std::__distance` with `I = cpp20_input_iterator`, resulting in a hard substitution error: `no type named 'difference_type' in 'std::iterator_traits<cpp20_input_iterator<int *>>'`, as captured by the tests `range.pass.cpp` and `iterator_sentinel.pass.cpp` (see the full log: [stage1 (generic-modules, clang-21, clang++-21)](https://github.com/llvm/llvm-project/actions/runs/15912723755/job/44883824080?pr=133612#logs)).
The root cause for this error is that `cpp20_input_iterator` does not have the member type `iterator_category` (although it provides the `iterator_concept` member type), leading to a hard substitution failure in `std::iterator_traits<cpp20_input_iterator<Iter>>::difference_type` (See [A reproducer](https://godbolt.org/z/sdxevGM5z)).
To resolve this, I still need the above internal definition of `__iter_distance_t`, which is used as the return type of the internal function `std::__distance`. This allows both the non-range `std::distance` and range version `std::ranges::distance` to share the same implementation and optimization. However, to handle the potential mismatch between `iterator_traits<I>::difference_type` and `iter_difference_t<I>`, I explicitly specified `iterator_traits<I>::difference_type` as the return type of `std::distance` and `iter_difference_t<I>` as the return type of `std::ranges::distance`. This way the underlying implicit conversion would automatically take place when there is a type mismatch.
> Basically, we should try re-defining `__iter_diff_t` in `iterator_traits.h` as:
>
> ```c++
> #if _LIBCPP_STD_VER >= 20
> template <class _Iter>
> using __iter_diff_t _LIBCPP_NODEBUG = std::iter_difference_t<_Iter>;
> #else
> template <class _Iter>
> using __iter_diff_t _LIBCPP_NODEBUG = typename iterator_traits<_Iter>::difference_type;
> #endif
> ```
>
> And if that's not possible for some reason, it would be interesting to understand why.
I am a bit hesitant to make this change because `__iter_diff_t` is currently **unconditionally** defined as `iterator_traits<_Iter>::difference_type`, and it is used by many algorithms. Making this change would introduce the potential type mismatch to many algorithms. My current approach with the introduction of `__iter_distance_t` ensures that the potential type mismatch is confined and handled within `distance.h`.
https://github.com/llvm/llvm-project/pull/133612
More information about the libcxx-commits
mailing list