libstdc++: Change [range.iter.op] functions to function objects [PR 100768]

The standard specifies std::ranges::distance etc as function templates,
but it also requires them to not be found by ADL, and to suppress ADL
when normal unqualified lookup does find them. That means they need to
be function objects.

libstdc++-v3/ChangeLog:

	PR libstdc++/100768
	* include/bits/ranges_base.h (advance, distance, next, prev):
	Replace function templates with function objects.
	* testsuite/24_iterators/headers/iterator/synopsis_c++20.cc:
	Adjust for changes to function objects.
	* testsuite/std/ranges/adaptors/elements.cc: Add using
	declarations for names from namespace ranges.
	* testsuite/std/ranges/adaptors/transform.cc: Likewise.
	* testsuite/24_iterators/range_operations/100768.cc: New test.
This commit is contained in:
Jonathan Wakely 2021-05-26 17:32:53 +01:00
parent 7f0cfeb1ac
commit a49a045b92
5 changed files with 336 additions and 201 deletions

View File

@ -674,197 +674,213 @@ namespace ranges
// [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;
}
}
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
{
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;
++__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 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;
}
}
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, 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;
template<input_or_output_iterator _It>
constexpr _It
next(_It __x)
{
++__x;
return __x;
}
// 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<input_or_output_iterator _It>
constexpr _It
next(_It __x, iter_difference_t<_It> __n)
{
ranges::advance(__x, __n);
return __x;
}
inline constexpr __advance_fn advance{};
template<input_or_output_iterator _It, sentinel_for<_It> _Sent>
constexpr _It
next(_It __x, _Sent __bound)
{
ranges::advance(__x, __bound);
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, iter_difference_t<_It> __n, _Sent __bound)
{
ranges::advance(__x, __n, __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<bidirectional_iterator _It>
constexpr _It
prev(_It __x)
{
--__x;
return __x;
}
inline constexpr __distance_fn distance{};
template<bidirectional_iterator _It>
constexpr _It
prev(_It __x, iter_difference_t<_It> __n)
{
ranges::advance(__x, -__n);
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, _It __bound)
{
ranges::advance(__x, -__n, __bound);
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<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

View File

@ -31,26 +31,11 @@ namespace std
namespace ranges
{
template<input_or_output_iterator I, sentinel_for<I> S>
constexpr iter_difference_t<I> distance(I first, S last);
template<range R>
constexpr range_difference_t<R> distance(R&& r);
template<input_or_output_iterator I>
constexpr I next(I x);
template<input_or_output_iterator I>
constexpr I next(I x, iter_difference_t<I> n);
template<input_or_output_iterator I, sentinel_for<I> S>
constexpr I next(I x, S bound);
template<input_or_output_iterator I, sentinel_for<I> S>
constexpr I next(I x, iter_difference_t<I> n, S bound);
template<bidirectional_iterator I>
constexpr I prev(I x);
template<bidirectional_iterator I>
constexpr I prev(I x, iter_difference_t<I> n);
template<bidirectional_iterator I>
constexpr I prev(I x, iter_difference_t<I> n, I bound);
// These are function objects of unspecified type.
auto& _distance = distance;
auto& _advance = advance;
auto& _next = next;
auto& _prev = prev;
}
template<semiregular S> class move_sentinel;

View File

@ -0,0 +1,128 @@
// 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);
}

View File

@ -76,6 +76,9 @@ struct X
void
test03()
{
using ranges::next;
using ranges::begin;
// LWG 3483
std::pair<int, X> x[3];
__gnu_test::test_forward_range<std::pair<int, X>> r(x);

View File

@ -132,6 +132,9 @@ struct Y
void
test06()
{
using ranges::next;
using ranges::begin;
// LWG 3483
Y y[3];
__gnu_test::test_forward_range<Y> r(y);