libstdc++: Specialize std::disable_sized_sentinel_for for std::move_iterator [PR116549]

LWG 3736 added a partial specialization of this variable template for
two std::move_iterator types. This is needed for the case where the
types satisfy std::sentinel_for and are subtractable, but do not model
the semantics requirements of std::sized_sentinel_for.

libstdc++-v3/ChangeLog:

	PR libstdc++/116549
	* include/bits/stl_iterator.h (disable_sized_sentinel_for):
	Define specialization for two move_iterator types, as per LWG
	3736.
	* testsuite/24_iterators/move_iterator/lwg3736.cc: New test.
This commit is contained in:
Jonathan Wakely
2024-09-02 11:29:13 +01:00
committed by Jonathan Wakely
parent ef0c4482ca
commit 819deae0a5
2 changed files with 60 additions and 0 deletions

View File

@@ -1822,6 +1822,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ return _ReturnType(__i); }
#if __cplusplus > 201703L && __glibcxx_concepts
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 3736. move_iterator missing disable_sized_sentinel_for specialization
template<typename _Iterator1, typename _Iterator2>
requires (!sized_sentinel_for<_Iterator1, _Iterator2>)
inline constexpr bool
disable_sized_sentinel_for<move_iterator<_Iterator1>,
move_iterator<_Iterator2>> = true;
// [iterators.common] Common iterators
namespace __detail

View File

@@ -0,0 +1,52 @@
// { dg-do compile { target c++20 } }
// 3736. move_iterator missing disable_sized_sentinel_for specialization
#include <iterator>
template<typename Iter> using MoveIter = std::move_iterator<Iter>;
using std::sized_sentinel_for;
using std::disable_sized_sentinel_for;
// These assertions always passed, even without LWG 3736:
static_assert(sized_sentinel_for<MoveIter<int*>, MoveIter<int*>>);
static_assert(sized_sentinel_for<MoveIter<int*>, MoveIter<const int*>>);
static_assert(not sized_sentinel_for<MoveIter<int*>, MoveIter<long*>>);
static_assert(not sized_sentinel_for<MoveIter<int*>, std::default_sentinel_t>);
static_assert(not disable_sized_sentinel_for<MoveIter<int*>, MoveIter<int*>>);
// These types don't satisfy sized_sentinel_for anyway (because the subtraction
// is ill-formed) but LWG 3736 makes the variable template explicitly false:
static_assert(disable_sized_sentinel_for<MoveIter<int*>, MoveIter<long*>>);
struct Iter
{
using iterator_category = std::random_access_iterator_tag;
using value_type = int;
using pointer = int*;
using reference = int&;
using difference_type = long;
Iter() = default;
Iter& operator++();
Iter operator++(int);
Iter& operator--();
Iter operator--(int);
reference operator*() const;
pointer operator->() const;
Iter& operator+=(difference_type);
Iter& operator-=(difference_type);
friend Iter operator+(Iter, difference_type);
friend Iter operator+(difference_type, Iter);
friend Iter operator-(Iter, difference_type);
friend difference_type operator-(Iter, Iter);
bool operator==(Iter) const;
};
// Specialize the variable template so that Iter is not its own sized sentinel:
template<> constexpr bool std::disable_sized_sentinel_for<Iter, Iter> = true;
static_assert( not sized_sentinel_for<Iter, Iter> );
// LWG 3736 means that affects std::move_iterator<Iter> as well:
static_assert( not sized_sentinel_for<MoveIter<Iter>, MoveIter<Iter>> );