// [range.iter.ops] range iterator operations
- template<input_or_output_iterator _It>
- constexpr void
- advance(_It& __it, iter_difference_t<_It> __n)
- {
- if constexpr (random_access_iterator<_It>)
- __it += __n;
- else if constexpr (bidirectional_iterator<_It>)
- {
- if (__n > 0)
- {
- do
- {
- ++__it;
- }
- while (--__n);
- }
- else if (__n < 0)
- {
- do
- {
- --__it;
- }
- while (++__n);
- }
- }
- else
- {
- // cannot decrement a non-bidirectional iterator
- __glibcxx_assert(__n >= 0);
- while (__n-- > 0)
- ++__it;
- }
- }
-
- template<input_or_output_iterator _It, sentinel_for<_It> _Sent>
- constexpr void
- advance(_It& __it, _Sent __bound)
- {
- if constexpr (assignable_from<_It&, _Sent>)
- __it = std::move(__bound);
- else if constexpr (sized_sentinel_for<_Sent, _It>)
- ranges::advance(__it, __bound - __it);
- else
- {
- while (__it != __bound)
- ++__it;
- }
- }
+ struct __advance_fn
+ {
+ template<input_or_output_iterator _It>
+ constexpr void
+ operator()(_It& __it, iter_difference_t<_It> __n) const
+ {
+ if constexpr (random_access_iterator<_It>)
+ __it += __n;
+ else if constexpr (bidirectional_iterator<_It>)
+ {
+ if (__n > 0)
+ {
+ do
+ {
+ ++__it;
+ }
+ while (--__n);
+ }
+ else if (__n < 0)
+ {
+ do
+ {
+ --__it;
+ }
+ while (++__n);
+ }
+ }
+ else
+ {
+ // cannot decrement a non-bidirectional iterator
+ __glibcxx_assert(__n >= 0);
+ while (__n-- > 0)
+ ++__it;
+ }
+ }
- template<input_or_output_iterator _It, sentinel_for<_It> _Sent>
- constexpr iter_difference_t<_It>
- advance(_It& __it, iter_difference_t<_It> __n, _Sent __bound)
- {
- if constexpr (sized_sentinel_for<_Sent, _It>)
- {
- const auto __diff = __bound - __it;
-#ifdef __cpp_lib_is_constant_evaluated
- if (std::is_constant_evaluated()
- && !(__n == 0 || __diff == 0 || (__n < 0 == __diff < 0)))
- throw "inconsistent directions for distance and bound";
-#endif
- // n and bound must not lead in opposite directions:
- __glibcxx_assert(__n == 0 || __diff == 0 || (__n < 0 == __diff < 0));
- const auto __absdiff = __diff < 0 ? -__diff : __diff;
- const auto __absn = __n < 0 ? -__n : __n;;
- if (__absn >= __absdiff)
- {
- ranges::advance(__it, __bound);
- return __n - __diff;
- }
- else
- {
- ranges::advance(__it, __n);
- return 0;
- }
- }
- else if (__it == __bound || __n == 0)
- return iter_difference_t<_It>(0);
- else if (__n > 0)
- {
- iter_difference_t<_It> __m = 0;
- do
- {
+ template<input_or_output_iterator _It, sentinel_for<_It> _Sent>
+ constexpr void
+ operator()(_It& __it, _Sent __bound) const
+ {
+ if constexpr (assignable_from<_It&, _Sent>)
+ __it = std::move(__bound);
+ else if constexpr (sized_sentinel_for<_Sent, _It>)
+ (*this)(__it, __bound - __it);
+ else
+ {
+ while (__it != __bound)
++__it;
- ++__m;
- }
- while (__m != __n && __it != __bound);
- return __n - __m;
- }
- else if constexpr (bidirectional_iterator<_It> && same_as<_It, _Sent>)
- {
- iter_difference_t<_It> __m = 0;
- do
- {
- --__it;
- --__m;
- }
- while (__m != __n && __it != __bound);
- return __n - __m;
- }
- else
- {
- // cannot decrement a non-bidirectional iterator
- __glibcxx_assert(__n >= 0);
- return __n;
- }
- }
+ }
+ }
- template<input_or_output_iterator _It, sentinel_for<_It> _Sent>
- constexpr iter_difference_t<_It>
- distance(_It __first, _Sent __last)
- {
- if constexpr (sized_sentinel_for<_Sent, _It>)
- return __last - __first;
- else
- {
- iter_difference_t<_It> __n = 0;
- while (__first != __last)
- {
- ++__first;
- ++__n;
- }
- return __n;
- }
- }
+ template<input_or_output_iterator _It, sentinel_for<_It> _Sent>
+ constexpr iter_difference_t<_It>
+ operator()(_It& __it, iter_difference_t<_It> __n, _Sent __bound) const
+ {
+ if constexpr (sized_sentinel_for<_Sent, _It>)
+ {
+ const auto __diff = __bound - __it;
+
+ // n and bound must not lead in opposite directions:
+ __glibcxx_assert(__n == 0 || __diff == 0 || (__n < 0 == __diff < 0));
+ const auto __absdiff = __diff < 0 ? -__diff : __diff;
+ const auto __absn = __n < 0 ? -__n : __n;;
+ if (__absn >= __absdiff)
+ {
+ (*this)(__it, __bound);
+ return __n - __diff;
+ }
+ else
+ {
+ (*this)(__it, __n);
+ return 0;
+ }
+ }
+ else if (__it == __bound || __n == 0)
+ return iter_difference_t<_It>(0);
+ else if (__n > 0)
+ {
+ iter_difference_t<_It> __m = 0;
+ do
+ {
+ ++__it;
+ ++__m;
+ }
+ while (__m != __n && __it != __bound);
+ return __n - __m;
+ }
+ else if constexpr (bidirectional_iterator<_It> && same_as<_It, _Sent>)
+ {
+ iter_difference_t<_It> __m = 0;
+ do
+ {
+ --__it;
+ --__m;
+ }
+ while (__m != __n && __it != __bound);
+ return __n - __m;
+ }
+ else
+ {
+ // cannot decrement a non-bidirectional iterator
+ __glibcxx_assert(__n >= 0);
+ return __n;
+ }
+ }
+ };
- template<range _Range>
- constexpr range_difference_t<_Range>
- distance(_Range&& __r)
- {
- if constexpr (sized_range<_Range>)
- return static_cast<range_difference_t<_Range>>(ranges::size(__r));
- else
- return ranges::distance(ranges::begin(__r), ranges::end(__r));
- }
-
- template<input_or_output_iterator _It>
- constexpr _It
- next(_It __x)
- {
- ++__x;
- return __x;
- }
+ inline constexpr __advance_fn advance{};
- template<input_or_output_iterator _It>
- constexpr _It
- next(_It __x, iter_difference_t<_It> __n)
- {
- ranges::advance(__x, __n);
- return __x;
- }
+ struct __distance_fn
+ {
+ template<input_or_output_iterator _It, sentinel_for<_It> _Sent>
+ constexpr iter_difference_t<_It>
+ operator()(_It __first, _Sent __last) const
+ {
+ if constexpr (sized_sentinel_for<_Sent, _It>)
+ return __last - __first;
+ else
+ {
+ iter_difference_t<_It> __n = 0;
+ while (__first != __last)
+ {
+ ++__first;
+ ++__n;
+ }
+ return __n;
+ }
+ }
- template<input_or_output_iterator _It, sentinel_for<_It> _Sent>
- constexpr _It
- next(_It __x, _Sent __bound)
- {
- ranges::advance(__x, __bound);
- return __x;
- }
+ template<range _Range>
+ constexpr range_difference_t<_Range>
+ operator()(_Range&& __r) const
+ {
+ if constexpr (sized_range<_Range>)
+ return static_cast<range_difference_t<_Range>>(ranges::size(__r));
+ else
+ return (*this)(ranges::begin(__r), ranges::end(__r));
+ }
+ };
- template<input_or_output_iterator _It, sentinel_for<_It> _Sent>
- constexpr _It
- next(_It __x, iter_difference_t<_It> __n, _Sent __bound)
- {
- ranges::advance(__x, __n, __bound);
- return __x;
- }
+ inline constexpr __distance_fn distance{};
- template<bidirectional_iterator _It>
- constexpr _It
- prev(_It __x)
- {
- --__x;
- return __x;
- }
+ struct __next_fn
+ {
+ template<input_or_output_iterator _It>
+ constexpr _It
+ operator()(_It __x) const
+ {
+ ++__x;
+ return __x;
+ }
- template<bidirectional_iterator _It>
- constexpr _It
- prev(_It __x, iter_difference_t<_It> __n)
- {
- ranges::advance(__x, -__n);
- return __x;
- }
+ template<input_or_output_iterator _It>
+ constexpr _It
+ operator()(_It __x, iter_difference_t<_It> __n) const
+ {
+ ranges::advance(__x, __n);
+ return __x;
+ }
- template<bidirectional_iterator _It>
- constexpr _It
- prev(_It __x, iter_difference_t<_It> __n, _It __bound)
- {
- ranges::advance(__x, -__n, __bound);
- return __x;
- }
+ template<input_or_output_iterator _It, sentinel_for<_It> _Sent>
+ constexpr _It
+ operator()(_It __x, _Sent __bound) const
+ {
+ ranges::advance(__x, __bound);
+ return __x;
+ }
+
+ template<input_or_output_iterator _It, sentinel_for<_It> _Sent>
+ constexpr _It
+ operator()(_It __x, iter_difference_t<_It> __n, _Sent __bound) const
+ {
+ ranges::advance(__x, __n, __bound);
+ return __x;
+ }
+ };
+
+ inline constexpr __next_fn next{};
+
+ struct __prev_fn
+ {
+ template<bidirectional_iterator _It>
+ constexpr _It
+ operator()(_It __x) const
+ {
+ --__x;
+ return __x;
+ }
+
+ template<bidirectional_iterator _It>
+ constexpr _It
+ operator()(_It __x, iter_difference_t<_It> __n) const
+ {
+ ranges::advance(__x, -__n);
+ return __x;
+ }
+
+ template<bidirectional_iterator _It>
+ constexpr _It
+ operator()(_It __x, iter_difference_t<_It> __n, _It __bound) const
+ {
+ ranges::advance(__x, -__n, __bound);
+ return __x;
+ }
+ };
+
+ inline constexpr __prev_fn prev{};
/// Type returned by algorithms instead of a dangling iterator or subrange.
struct dangling
--- /dev/null
+// Copyright (C) 2021 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++20" }
+// { dg-do compile { target c++20 } }
+
+// PR libstdc++/100768 - Range iterator operations should be function objects
+
+#include <iterator>
+#include <ranges>
+
+namespace ns1
+{
+ struct R { };
+ void check_adl(R) { }
+}
+
+namespace ns2
+{
+ using ns1::R;
+
+ struct A { };
+
+ template<typename I>
+ R advance(I, ...) { return R{}; }
+
+ template<typename I>
+ R distance(I, ...) { return R{}; }
+
+ template<typename I>
+ R next(I, ...) { return R{}; }
+
+ template<typename I>
+ R prev(I, ...) { return R{}; }
+}
+
+template<typename T, typename U> struct associated { };
+
+void
+test02()
+{
+ // This type has both ns2 and std::ranges as associated namespaces.
+ using X = associated<ns2::A, std::ranges::dangling>;
+
+ X range[1];
+ X* iter = range;
+ X* const sentinel = iter + 1;
+
+ // [range.iter.op.general] p2 says: "The function templates defined in
+ // [range.iter.ops] are not found by argument-dependent name lookup."
+ //
+ // If we do not meet that requirement then the following will find those
+ // function templates (because std::ranges is an associated namespace),
+ // and the calls to check_adl will be ill-formed.
+ check_adl( advance(iter, 1) );
+ check_adl( advance(iter, 1, sentinel) );
+ check_adl( distance(iter, sentinel) );
+ check_adl( distance(range) );
+ check_adl( next(iter) );
+ check_adl( next(iter, 1) );
+ check_adl( next(iter, sentinel) );
+ check_adl( next(iter, 1, sentinel) );
+ check_adl( prev(iter) );
+ check_adl( prev(iter, 1) );
+ check_adl( prev(iter, 1, sentinel) );
+}
+
+namespace ns3
+{
+ struct A { };
+
+ void advance(A*, int) = delete;
+ void advance(A*, int, A*) = delete;
+
+ void distance(A*, A*) = delete;
+ void distance(A(&)[1]) = delete;
+
+ void next(A*) = delete;
+ void next(A*, int) = delete;
+ void next(A*, A*) = delete;
+ void next(A*, int, A*) = delete;
+
+ void prev(A*) = delete;
+ void prev(A*, int) = delete;
+ void prev(A*, int, A*) = delete;
+}
+
+void
+test01()
+{
+ ns3::A range[1];
+ ns3::A* iter = range;
+ ns3::A* const sentinel = iter + 1;
+
+ // [range.iter.op.general] p2 also says: "When found by unqualified name
+ // lookup for the postfix-expression in a function call, they inhibit
+ // argument-dependent name lookup."
+ //
+ // If we do not meet that requirement then the following will find the
+ // deleted overloads in namespace ns3 (because it is an associated namespace
+ // and those functions are exact matches for the arguments).
+ using namespace std::ranges;
+ advance(iter, 1);
+ advance(iter, 3, sentinel);
+ distance(iter, sentinel);
+ distance(range);
+ next(iter);
+ next(iter, -1);
+ next(iter, sentinel);
+ next(iter, 5, sentinel);
+ prev(iter);
+ prev(iter, 0);
+ prev(iter, 0, sentinel);
+}