[libc++][ranges] Implement changes to reverse_iterator from One Ranges Proposal.

Changes in [P0896](https://wg21.link/p0896):
- add `disable_sized_sentinel_for`;
- add `iter_move` and `iter_swap`;
- add a `requires` clause to the `operator->`;
- add `iterator_concept`;
- check that the `Iterator` template parameter is a bidirectional
  iterator;
- add constraints to all comparison operators;
- change the definitions of `iterator_category`, `value_type`,
  `difference_type` and `reference` (changes to `iterator_category` were
  already implemented).

Also add a few forgotten things to the `reverse_iterator` synopsis
(notably the spaceship operator).

Differential Revision: https://reviews.llvm.org/D120180
This commit is contained in:
Konstantin Varlamov 2022-03-17 19:57:39 -07:00
parent 07998f6d75
commit 658957c79a
14 changed files with 967 additions and 137 deletions

View File

@ -75,7 +75,7 @@ Section,Description,Dependencies,Assignee,Complete
| `ranges::next <https://llvm.org/D102563>`_
| `ranges::prev <https://llvm.org/D102564>`_",[iterator.concepts],Christopher Di Bella and Arthur O'Dwyer,✅
`[predef.iterators] <https://wg21.link/predef.iterators>`_,"
| Updates to reverse_iterator
| `Updates to reverse_iterator <https://llvm.org/D120180>`_
| `Updates to back_insert_iterator <https://llvm.org/D103273>`_
| `Updates to front_insert_iterator <https://llvm.org/D103273>`_
| `Updates to move_iterator <https://llvm.org/D117656>`_","| [iterator.concepts]

1 Section Description Dependencies Assignee Complete
75
76
77
78
79
80
81

View File

@ -141,8 +141,9 @@ public:
#if _LIBCPP_STD_VER > 17
// The `cpp17-*-iterator` exposition-only concepts are easily confused with the Cpp17*Iterator tables,
// so they've been banished to a namespace that makes it obvious they have a niche use-case.
// The `cpp17-*-iterator` exposition-only concepts have very similar names to the `Cpp17*Iterator` named requirements
// from `[iterator.cpp17]`. To avoid confusion between the two, the exposition-only concepts have been banished to
// a "detail" namespace indicating they have a niche use-case.
namespace __iterator_traits_detail {
template<class _Ip>
concept __cpp17_iterator =

View File

@ -12,10 +12,18 @@
#include <__compare/compare_three_way_result.h>
#include <__compare/three_way_comparable.h>
#include <__concepts/convertible_to.h>
#include <__config>
#include <__iterator/concepts.h>
#include <__iterator/incrementable_traits.h>
#include <__iterator/iter_move.h>
#include <__iterator/iter_swap.h>
#include <__iterator/iterator.h>
#include <__iterator/iterator_traits.h>
#include <__iterator/prev.h>
#include <__iterator/readable_traits.h>
#include <__memory/addressof.h>
#include <__utility/move.h>
#include <type_traits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@ -41,22 +49,31 @@ private:
_Iter __t; // no longer used as of LWG #2360, not removed due to ABI break
#endif
#if _LIBCPP_STD_VER > 17
static_assert(__is_cpp17_bidirectional_iterator<_Iter>::value || bidirectional_iterator<_Iter>,
"reverse_iterator<It> requires It to be a bidirectional iterator.");
#endif // _LIBCPP_STD_VER > 17
protected:
_Iter current;
public:
typedef _Iter iterator_type;
typedef typename iterator_traits<_Iter>::difference_type difference_type;
typedef typename iterator_traits<_Iter>::reference reference;
typedef typename iterator_traits<_Iter>::pointer pointer;
typedef _If<__is_cpp17_random_access_iterator<_Iter>::value,
random_access_iterator_tag,
typename iterator_traits<_Iter>::iterator_category> iterator_category;
typedef typename iterator_traits<_Iter>::value_type value_type;
using iterator_type = _Iter;
#if _LIBCPP_STD_VER > 17
typedef _If<__is_cpp17_random_access_iterator<_Iter>::value,
using iterator_category = _If<__is_cpp17_random_access_iterator<_Iter>::value,
random_access_iterator_tag,
bidirectional_iterator_tag> iterator_concept;
typename iterator_traits<_Iter>::iterator_category>;
using pointer = typename iterator_traits<_Iter>::pointer;
#if _LIBCPP_STD_VER > 17
using iterator_concept = _If<__is_cpp17_random_access_iterator<_Iter>::value,
random_access_iterator_tag,
bidirectional_iterator_tag>;
using value_type = iter_value_t<_Iter>;
using difference_type = iter_difference_t<_Iter>;
using reference = iter_reference_t<_Iter>;
#else
using value_type = typename iterator_traits<_Iter>::value_type;
using difference_type = typename iterator_traits<_Iter>::difference_type;
using reference = typename iterator_traits<_Iter>::reference;
#endif
#ifndef _LIBCPP_ABI_NO_ITERATOR_BASES
@ -114,8 +131,25 @@ public:
_Iter base() const {return current;}
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
reference operator*() const {_Iter __tmp = current; return *--__tmp;}
#if _LIBCPP_STD_VER > 17
_LIBCPP_INLINE_VISIBILITY
constexpr pointer operator->() const
requires is_pointer_v<_Iter> || requires(const _Iter i) { i.operator->(); }
{
if constexpr (is_pointer_v<_Iter>) {
return std::prev(current);
} else {
return std::prev(current).operator->();
}
}
#else
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
pointer operator->() const {return _VSTD::addressof(operator*());}
pointer operator->() const {
return std::addressof(operator*());
}
#endif // _LIBCPP_STD_VER > 17
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
reverse_iterator& operator++() {--current; return *this;}
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
@ -134,12 +168,38 @@ public:
reverse_iterator& operator-=(difference_type __n) {current += __n; return *this;}
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
reference operator[](difference_type __n) const {return *(*this + __n);}
#if _LIBCPP_STD_VER > 17
_LIBCPP_HIDE_FROM_ABI friend constexpr
iter_rvalue_reference_t<_Iter> iter_move(const reverse_iterator& __i)
noexcept(is_nothrow_copy_constructible_v<_Iter> &&
noexcept(ranges::iter_move(--declval<_Iter&>()))) {
auto __tmp = __i.base();
return ranges::iter_move(--__tmp);
}
template <indirectly_swappable<_Iter> _Iter2>
_LIBCPP_HIDE_FROM_ABI friend constexpr
void iter_swap(const reverse_iterator& __x, const reverse_iterator<_Iter2>& __y)
noexcept(is_nothrow_copy_constructible_v<_Iter> &&
is_nothrow_copy_constructible_v<_Iter2> &&
noexcept(ranges::iter_swap(--declval<_Iter&>(), --declval<_Iter2&>()))) {
auto __xtmp = __x.base();
auto __ytmp = __y.base();
ranges::iter_swap(--__xtmp, --__ytmp);
}
#endif // _LIBCPP_STD_VER > 17
};
template <class _Iter1, class _Iter2>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
bool
operator==(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y)
#if _LIBCPP_STD_VER > 17
requires requires {
{ __x.base() == __y.base() } -> convertible_to<bool>;
}
#endif // _LIBCPP_STD_VER > 17
{
return __x.base() == __y.base();
}
@ -148,6 +208,11 @@ template <class _Iter1, class _Iter2>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
bool
operator<(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y)
#if _LIBCPP_STD_VER > 17
requires requires {
{ __x.base() > __y.base() } -> convertible_to<bool>;
}
#endif // _LIBCPP_STD_VER > 17
{
return __x.base() > __y.base();
}
@ -156,6 +221,11 @@ template <class _Iter1, class _Iter2>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
bool
operator!=(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y)
#if _LIBCPP_STD_VER > 17
requires requires {
{ __x.base() != __y.base() } -> convertible_to<bool>;
}
#endif // _LIBCPP_STD_VER > 17
{
return __x.base() != __y.base();
}
@ -164,6 +234,11 @@ template <class _Iter1, class _Iter2>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
bool
operator>(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y)
#if _LIBCPP_STD_VER > 17
requires requires {
{ __x.base() < __y.base() } -> convertible_to<bool>;
}
#endif // _LIBCPP_STD_VER > 17
{
return __x.base() < __y.base();
}
@ -172,6 +247,11 @@ template <class _Iter1, class _Iter2>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
bool
operator>=(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y)
#if _LIBCPP_STD_VER > 17
requires requires {
{ __x.base() <= __y.base() } -> convertible_to<bool>;
}
#endif // _LIBCPP_STD_VER > 17
{
return __x.base() <= __y.base();
}
@ -180,6 +260,11 @@ template <class _Iter1, class _Iter2>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
bool
operator<=(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y)
#if _LIBCPP_STD_VER > 17
requires requires {
{ __x.base() >= __y.base() } -> convertible_to<bool>;
}
#endif // _LIBCPP_STD_VER > 17
{
return __x.base() >= __y.base();
}
@ -221,6 +306,12 @@ operator+(typename reverse_iterator<_Iter>::difference_type __n, const reverse_i
return reverse_iterator<_Iter>(__x.base() - __n);
}
#if _LIBCPP_STD_VER > 17
template <class _Iter1, class _Iter2>
requires (!sized_sentinel_for<_Iter1, _Iter2>)
inline constexpr bool disable_sized_sentinel_for<reverse_iterator<_Iter1>, reverse_iterator<_Iter2>> = true;
#endif // _LIBCPP_STD_VER > 17
#if _LIBCPP_STD_VER > 11
template <class _Iter>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14

View File

@ -225,10 +225,17 @@ class reverse_iterator
protected:
Iterator current;
public:
typedef Iterator iterator_type;
typedef typename iterator_traits<Iterator>::difference_type difference_type;
typedef typename iterator_traits<Iterator>::reference reference;
typedef typename iterator_traits<Iterator>::pointer pointer;
using iterator_type = Iterator;
using iterator_concept = see below; // since C++20
using iterator_category = typename iterator_traits<Iterator>::iterator_category; // since C++17, until C++20
using iterator_category = see below; // since C++20
using value_type = typename iterator_traits<Iterator>::value_type; // since C++17, until C++20
using value_type = iter_value_t<Iterator>; // since C++20
using difference_type = typename iterator_traits<Iterator>::difference_type; // until C++20
using difference_type = iter_difference_t<Iterator>; // since C++20
using pointer = typename iterator_traits<Iterator>::pointer;
using reference = typename iterator_traits<Iterator>::reference; // until C++20
using reference = iter_reference_t<Iterator>; // since C++20
constexpr reverse_iterator();
constexpr explicit reverse_iterator(Iterator x);
@ -236,7 +243,8 @@ public:
template <class U> constexpr reverse_iterator& operator=(const reverse_iterator<U>& u);
constexpr Iterator base() const;
constexpr reference operator*() const;
constexpr pointer operator->() const;
constexpr pointer operator->() const; // until C++20
constexpr pointer operator->() const requires see below; // since C++20
constexpr reverse_iterator& operator++();
constexpr reverse_iterator operator++(int);
constexpr reverse_iterator& operator--();
@ -245,7 +253,14 @@ public:
constexpr reverse_iterator& operator+=(difference_type n);
constexpr reverse_iterator operator- (difference_type n) const;
constexpr reverse_iterator& operator-=(difference_type n);
constexpr reference operator[](difference_type n) const;
constexpr unspecified operator[](difference_type n) const;
friend constexpr iter_rvalue_reference_t<Iterator>
iter_move(const reverse_iterator& i) noexcept(see below);
template<indirectly_swappable<Iterator> Iterator2>
friend constexpr void
iter_swap(const reverse_iterator& x,
const reverse_iterator<Iterator2>& y) noexcept(see below);
};
template <class Iterator1, class Iterator2>
@ -254,11 +269,11 @@ operator==(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator
template <class Iterator1, class Iterator2>
constexpr bool // constexpr in C++17
operator<(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
operator!=(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
template <class Iterator1, class Iterator2>
constexpr bool // constexpr in C++17
operator!=(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
operator<(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
template <class Iterator1, class Iterator2>
constexpr bool // constexpr in C++17
@ -266,11 +281,16 @@ operator>(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2
template <class Iterator1, class Iterator2>
constexpr bool // constexpr in C++17
operator>=(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
operator<=(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
template <class Iterator1, class Iterator2>
constexpr bool // constexpr in C++17
operator<=(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
operator>=(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
template<class Iterator1, three_way_comparable_with<Iterator1> Iterator2>
constexpr compare_three_way_result_t<Iterator1, Iterator2>
operator<=>(const reverse_iterator<Iterator1>& x,
const reverse_iterator<Iterator2>& y);
template <class Iterator1, class Iterator2>
constexpr auto
@ -285,6 +305,11 @@ operator+(typename reverse_iterator<Iterator>::difference_type n,
template <class Iterator>
constexpr reverse_iterator<Iterator> make_reverse_iterator(Iterator i); // C++14, constexpr in C++17
template<class Iterator1, class Iterator2>
requires (!sized_sentinel_for<Iterator1, Iterator2>)
inline constexpr bool disable_sized_sentinel_for<reverse_iterator<Iterator1>,
reverse_iterator<Iterator2>> = true;
template <class Container>
class back_insert_iterator
: public iterator<output_iterator_tag, void, void, void, void> // until C++17

View File

@ -0,0 +1,25 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-no-concepts
// <iterator>
// reverse_iterator
#include <iterator>
#include "test_iterators.h"
int main(int, char**) {
using BadIter = std::reverse_iterator<forward_iterator<int*>>;
BadIter i; //expected-error-re@*:* {{static_assert failed{{.*}} "reverse_iterator<It> requires It to be a bidirectional iterator."}}
return 0;
}

View File

@ -18,8 +18,6 @@ template<class I1>
constexpr bool common_reverse_iterator_checks() {
static_assert(std::indirectly_writable<I1, int>);
static_assert(std::sentinel_for<I1, I1>);
static_assert(std::sentinel_for<I1, std::reverse_iterator<float*>>);
static_assert(!std::sized_sentinel_for<I1, std::reverse_iterator<float*>>);
return true;
}
@ -55,3 +53,10 @@ static_assert( std::indirectly_movable_storable<reverse_contiguous_iterator, rev
static_assert( std::indirectly_copyable<reverse_contiguous_iterator, reverse_contiguous_iterator>);
static_assert( std::indirectly_copyable_storable<reverse_contiguous_iterator, reverse_contiguous_iterator>);
static_assert( std::indirectly_swappable<reverse_contiguous_iterator, reverse_contiguous_iterator>);
static_assert( std::equality_comparable_with<std::reverse_iterator<int*>, std::reverse_iterator<const int*>>);
static_assert(!std::equality_comparable_with<std::reverse_iterator<int*>, std::reverse_iterator<char*>>);
static_assert( std::totally_ordered_with<std::reverse_iterator<int*>, std::reverse_iterator<const int*>>);
static_assert(!std::totally_ordered_with<std::reverse_iterator<int*>, std::reverse_iterator<char*>>);
static_assert( std::three_way_comparable_with<std::reverse_iterator<int*>, std::reverse_iterator<const int*>>);
static_assert(!std::three_way_comparable_with<std::reverse_iterator<int*>, std::reverse_iterator<char*>>);

View File

@ -0,0 +1,196 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-no-concepts
// <iterator>
//
// reverse_iterator
//
// template <class Iterator1, class Iterator2>
// constexpr bool // constexpr in C++17
// operator==(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
//
// template <class Iterator1, class Iterator2>
// constexpr bool // constexpr in C++17
// operator!=(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
//
// template <class Iterator1, class Iterator2>
// constexpr bool // constexpr in C++17
// operator<(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
//
// template <class Iterator1, class Iterator2>
// constexpr bool // constexpr in C++17
// operator>(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
//
// template <class Iterator1, class Iterator2>
// constexpr bool // constexpr in C++17
// operator<=(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
//
// template <class Iterator1, class Iterator2>
// constexpr bool // constexpr in C++17
// operator>=(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
//
// template<class Iterator1, three_way_comparable_with<Iterator1> Iterator2>
// constexpr compare_three_way_result_t<Iterator1, Iterator2>
// operator<=>(const reverse_iterator<Iterator1>& x,
// const reverse_iterator<Iterator2>& y);
#include <iterator>
#include <cassert>
#include "test_macros.h"
struct IterBase {
using iterator_category = std::bidirectional_iterator_tag;
using value_type = int;
using difference_type = ptrdiff_t;
using pointer = int*;
using reference = int&;
reference operator*() const;
pointer operator->() const;
};
template<class T> concept HasEqual = requires (T t) { t == t; };
template<class T> concept HasNotEqual = requires (T t) { t != t; };
template<class T> concept HasLess = requires (T t) { t < t; };
template<class T> concept HasLessOrEqual = requires (T t) { t <= t; };
template<class T> concept HasGreater = requires (T t) { t > t; };
template<class T> concept HasGreaterOrEqual = requires (T t) { t >= t; };
template<class T> concept HasSpaceship = requires (T t) { t <=> t; };
// operator ==
struct NoEqualityCompIter : IterBase {
bool operator!=(NoEqualityCompIter) const;
bool operator<(NoEqualityCompIter) const;
bool operator>(NoEqualityCompIter) const;
bool operator<=(NoEqualityCompIter) const;
bool operator>=(NoEqualityCompIter) const;
};
static_assert( HasEqual<std::reverse_iterator<int*>>);
static_assert(!HasEqual<std::reverse_iterator<NoEqualityCompIter>>);
static_assert( HasNotEqual<std::reverse_iterator<NoEqualityCompIter>>);
static_assert( HasLess<std::reverse_iterator<NoEqualityCompIter>>);
static_assert( HasLessOrEqual<std::reverse_iterator<NoEqualityCompIter>>);
static_assert( HasGreater<std::reverse_iterator<NoEqualityCompIter>>);
static_assert( HasGreaterOrEqual<std::reverse_iterator<NoEqualityCompIter>>);
void Foo() {
std::reverse_iterator<NoEqualityCompIter> i;
(void)i;
}
// operator !=
struct NoInequalityCompIter : IterBase {
bool operator<(NoInequalityCompIter) const;
bool operator>(NoInequalityCompIter) const;
bool operator<=(NoInequalityCompIter) const;
bool operator>=(NoInequalityCompIter) const;
};
static_assert( HasNotEqual<std::reverse_iterator<int*>>);
static_assert(!HasNotEqual<std::reverse_iterator<NoInequalityCompIter>>);
static_assert(!HasEqual<std::reverse_iterator<NoInequalityCompIter>>);
static_assert( HasLess<std::reverse_iterator<NoInequalityCompIter>>);
static_assert( HasLessOrEqual<std::reverse_iterator<NoInequalityCompIter>>);
static_assert( HasGreater<std::reverse_iterator<NoInequalityCompIter>>);
static_assert( HasGreaterOrEqual<std::reverse_iterator<NoInequalityCompIter>>);
// operator <
struct NoGreaterCompIter : IterBase {
bool operator==(NoGreaterCompIter) const;
bool operator!=(NoGreaterCompIter) const;
bool operator<(NoGreaterCompIter) const;
bool operator<=(NoGreaterCompIter) const;
bool operator>=(NoGreaterCompIter) const;
};
static_assert( HasLess<std::reverse_iterator<int*>>);
static_assert(!HasLess<std::reverse_iterator<NoGreaterCompIter>>);
static_assert( HasEqual<std::reverse_iterator<NoGreaterCompIter>>);
static_assert( HasNotEqual<std::reverse_iterator<NoGreaterCompIter>>);
static_assert( HasLessOrEqual<std::reverse_iterator<NoGreaterCompIter>>);
static_assert( HasGreater<std::reverse_iterator<NoGreaterCompIter>>);
static_assert( HasGreaterOrEqual<std::reverse_iterator<NoGreaterCompIter>>);
// operator >
struct NoLessCompIter : IterBase {
bool operator==(NoLessCompIter) const;
bool operator!=(NoLessCompIter) const;
bool operator>(NoLessCompIter) const;
bool operator<=(NoLessCompIter) const;
bool operator>=(NoLessCompIter) const;
};
static_assert( HasGreater<std::reverse_iterator<int*>>);
static_assert(!HasGreater<std::reverse_iterator<NoLessCompIter>>);
static_assert( HasEqual<std::reverse_iterator<NoLessCompIter>>);
static_assert( HasNotEqual<std::reverse_iterator<NoLessCompIter>>);
static_assert( HasLess<std::reverse_iterator<NoLessCompIter>>);
static_assert( HasLessOrEqual<std::reverse_iterator<NoLessCompIter>>);
static_assert( HasGreaterOrEqual<std::reverse_iterator<NoLessCompIter>>);
// operator <=
struct NoGreaterOrEqualCompIter : IterBase {
bool operator==(NoGreaterOrEqualCompIter) const;
bool operator!=(NoGreaterOrEqualCompIter) const;
bool operator<(NoGreaterOrEqualCompIter) const;
bool operator>(NoGreaterOrEqualCompIter) const;
bool operator<=(NoGreaterOrEqualCompIter) const;
};
static_assert( HasLessOrEqual<std::reverse_iterator<int*>>);
static_assert(!HasLessOrEqual<std::reverse_iterator<NoGreaterOrEqualCompIter>>);
static_assert( HasEqual<std::reverse_iterator<NoGreaterOrEqualCompIter>>);
static_assert( HasNotEqual<std::reverse_iterator<NoGreaterOrEqualCompIter>>);
static_assert( HasLess<std::reverse_iterator<NoGreaterOrEqualCompIter>>);
static_assert( HasGreater<std::reverse_iterator<NoGreaterOrEqualCompIter>>);
static_assert( HasGreaterOrEqual<std::reverse_iterator<NoGreaterOrEqualCompIter>>);
// operator >=
struct NoLessOrEqualCompIter : IterBase {
bool operator==(NoLessOrEqualCompIter) const;
bool operator!=(NoLessOrEqualCompIter) const;
bool operator<(NoLessOrEqualCompIter) const;
bool operator>(NoLessOrEqualCompIter) const;
bool operator>=(NoLessOrEqualCompIter) const;
};
static_assert( HasGreaterOrEqual<std::reverse_iterator<int*>>);
static_assert(!HasGreaterOrEqual<std::reverse_iterator<NoLessOrEqualCompIter>>);
static_assert( HasEqual<std::reverse_iterator<NoLessOrEqualCompIter>>);
static_assert( HasNotEqual<std::reverse_iterator<NoLessOrEqualCompIter>>);
static_assert( HasLess<std::reverse_iterator<NoLessOrEqualCompIter>>);
static_assert( HasLessOrEqual<std::reverse_iterator<NoLessOrEqualCompIter>>);
static_assert( HasGreater<std::reverse_iterator<NoLessOrEqualCompIter>>);
// operator <=>
static_assert( std::three_way_comparable_with<int*, int*>);
static_assert( HasSpaceship<std::reverse_iterator<int*>>);
static_assert(!std::three_way_comparable_with<NoEqualityCompIter, NoEqualityCompIter>);
static_assert(!HasSpaceship<std::reverse_iterator<NoEqualityCompIter>>);
static_assert(!std::three_way_comparable_with<NoInequalityCompIter, NoInequalityCompIter>);
static_assert(!HasSpaceship<std::reverse_iterator<NoInequalityCompIter>>);
static_assert(!std::three_way_comparable_with<NoGreaterCompIter, NoGreaterCompIter>);
static_assert(!HasSpaceship<std::reverse_iterator<NoGreaterCompIter>>);
static_assert(!std::three_way_comparable_with<NoLessCompIter, NoLessCompIter>);
static_assert(!HasSpaceship<std::reverse_iterator<NoLessCompIter>>);
static_assert(!std::three_way_comparable_with<NoGreaterOrEqualCompIter, NoGreaterOrEqualCompIter>);
static_assert(!HasSpaceship<std::reverse_iterator<NoGreaterOrEqualCompIter>>);
static_assert(!std::three_way_comparable_with<NoLessOrEqualCompIter, NoLessOrEqualCompIter>);
static_assert(!HasSpaceship<std::reverse_iterator<NoLessOrEqualCompIter>>);

View File

@ -0,0 +1,53 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// <iterator>
//
// reverse_iterator
//
// pointer operator->() const;
#include <iterator>
#include <type_traits>
#include "test_iterators.h"
template <class T>
concept HasArrow = requires(T t) { t.operator->(); };
struct simple_bidirectional_iterator {
using iterator_category = std::bidirectional_iterator_tag;
using value_type = int;
using difference_type = int;
using pointer = int*;
using reference = int&;
reference operator*() const;
pointer operator->() const;
simple_bidirectional_iterator& operator++();
simple_bidirectional_iterator& operator--();
simple_bidirectional_iterator operator++(int);
simple_bidirectional_iterator operator--(int);
friend bool operator==(const simple_bidirectional_iterator&, const simple_bidirectional_iterator&);
};
static_assert( std::bidirectional_iterator<simple_bidirectional_iterator>);
static_assert(!std::random_access_iterator<simple_bidirectional_iterator>);
using PtrRI = std::reverse_iterator<int*>;
static_assert( HasArrow<PtrRI>);
using PtrLikeRI = std::reverse_iterator<simple_bidirectional_iterator>;
static_assert( HasArrow<PtrLikeRI>);
// `bidirectional_iterator` from `test_iterators.h` doesn't define `operator->`.
using NonPtrRI = std::reverse_iterator<bidirectional_iterator<int*>>;
static_assert(!HasArrow<NonPtrRI>);

View File

@ -0,0 +1,178 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// <iterator>
//
// reverse_iterator
//
// friend constexpr iter_rvalue_reference_t<Iterator>
// iter_move(const reverse_iterator& i) noexcept(see below);
#include <iterator>
#include <cassert>
#include <type_traits>
#include <utility>
#include "test_macros.h"
namespace adl {
struct Iterator {
using value_type = int;
using difference_type = ptrdiff_t;
value_type* ptr_ = nullptr;
int* iter_move_invocations_ = nullptr;
constexpr Iterator() = default;
constexpr explicit Iterator(int* p, int& iter_moves) : ptr_(p), iter_move_invocations_(&iter_moves) {}
constexpr value_type& operator*() const { return *ptr_; }
Iterator& operator++() { ++ptr_; return *this; }
Iterator operator++(int) {
Iterator prev = *this;
++ptr_;
return prev;
}
constexpr Iterator& operator--() { --ptr_; return *this; }
constexpr Iterator operator--(int) {
Iterator prev = *this;
--ptr_;
return prev;
}
constexpr friend value_type&& iter_move(Iterator iter) {
if (iter.iter_move_invocations_) {
++(*iter.iter_move_invocations_);
}
return std::move(*iter);
}
friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr_ == rhs.ptr_; }
};
} // namespace adl
constexpr bool test() {
// Can use `iter_move` with a regular array.
{
constexpr int N = 3;
int a[N] = {0, 1, 2};
std::reverse_iterator<int*> ri(a + N);
static_assert(std::same_as<decltype(iter_move(ri)), int&&>);
assert(iter_move(ri) == 2);
++ri;
assert(iter_move(ri) == 1);
}
// Ensure the `iter_move` customization point is being used.
{
constexpr int N = 3;
int a[N] = {0, 1, 2};
int iter_move_invocations = 0;
adl::Iterator i(a + N, iter_move_invocations);
std::reverse_iterator<adl::Iterator> ri(i);
int x = iter_move(ri);
assert(x == 2);
assert(iter_move_invocations == 1);
}
// Check the `noexcept` specification.
{
{
struct ThrowingCopyNoexceptDecrement {
using value_type = int;
using difference_type = ptrdiff_t;
ThrowingCopyNoexceptDecrement();
ThrowingCopyNoexceptDecrement(const ThrowingCopyNoexceptDecrement&);
int& operator*() const noexcept { static int x; return x; }
ThrowingCopyNoexceptDecrement& operator++();
ThrowingCopyNoexceptDecrement operator++(int);
ThrowingCopyNoexceptDecrement& operator--() noexcept;
ThrowingCopyNoexceptDecrement operator--(int) noexcept;
bool operator==(const ThrowingCopyNoexceptDecrement&) const = default;
};
static_assert(std::bidirectional_iterator<ThrowingCopyNoexceptDecrement>);
static_assert(!std::is_nothrow_copy_constructible_v<ThrowingCopyNoexceptDecrement>);
ASSERT_NOEXCEPT(std::ranges::iter_move(--std::declval<ThrowingCopyNoexceptDecrement&>()));
using RI = std::reverse_iterator<ThrowingCopyNoexceptDecrement>;
ASSERT_NOT_NOEXCEPT(iter_move(std::declval<RI>()));
}
{
struct NoexceptCopyThrowingDecrement {
using value_type = int;
using difference_type = ptrdiff_t;
NoexceptCopyThrowingDecrement();
NoexceptCopyThrowingDecrement(const NoexceptCopyThrowingDecrement&) noexcept;
int& operator*() const { static int x; return x; }
NoexceptCopyThrowingDecrement& operator++();
NoexceptCopyThrowingDecrement operator++(int);
NoexceptCopyThrowingDecrement& operator--();
NoexceptCopyThrowingDecrement operator--(int);
bool operator==(const NoexceptCopyThrowingDecrement&) const = default;
};
static_assert(std::bidirectional_iterator<NoexceptCopyThrowingDecrement>);
static_assert( std::is_nothrow_copy_constructible_v<NoexceptCopyThrowingDecrement>);
ASSERT_NOT_NOEXCEPT(std::ranges::iter_move(--std::declval<NoexceptCopyThrowingDecrement&>()));
using RI = std::reverse_iterator<NoexceptCopyThrowingDecrement>;
ASSERT_NOT_NOEXCEPT(iter_move(std::declval<RI>()));
}
{
struct NoexceptCopyAndDecrement {
using value_type = int;
using difference_type = ptrdiff_t;
NoexceptCopyAndDecrement();
NoexceptCopyAndDecrement(const NoexceptCopyAndDecrement&) noexcept;
int& operator*() const noexcept { static int x; return x; }
NoexceptCopyAndDecrement& operator++();
NoexceptCopyAndDecrement operator++(int);
NoexceptCopyAndDecrement& operator--() noexcept;
NoexceptCopyAndDecrement operator--(int) noexcept;
bool operator==(const NoexceptCopyAndDecrement&) const = default;
};
static_assert(std::bidirectional_iterator<NoexceptCopyAndDecrement>);
static_assert( std::is_nothrow_copy_constructible_v<NoexceptCopyAndDecrement>);
ASSERT_NOEXCEPT(std::ranges::iter_move(--std::declval<NoexceptCopyAndDecrement&>()));
using RI = std::reverse_iterator<NoexceptCopyAndDecrement>;
ASSERT_NOEXCEPT(iter_move(std::declval<RI>()));
}
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,190 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// <iterator>
//
// reverse_iterator
//
// template<indirectly_swappable<Iterator> Iterator2>
// friend constexpr void
// iter_swap(const reverse_iterator& x,
// const reverse_iterator<Iterator2>& y) noexcept(see below);
#include <iterator>
#include <cassert>
#include <type_traits>
#include <utility>
#include "test_macros.h"
namespace adl {
struct Iterator {
using value_type = int;
using difference_type = ptrdiff_t;
value_type* ptr_ = nullptr;
int* iter_swap_invocations_ = nullptr;
constexpr Iterator() = default;
constexpr explicit Iterator(int& iter_swaps) : iter_swap_invocations_(&iter_swaps) {}
value_type& operator*() const { return *ptr_; }
Iterator& operator++() { ++ptr_; return *this; }
Iterator operator++(int) {
Iterator prev = *this;
++ptr_;
return prev;
}
Iterator& operator--() { --ptr_; return *this; }
Iterator operator--(int) {
Iterator prev = *this;
--ptr_;
return prev;
}
constexpr friend void iter_swap(Iterator a, Iterator) {
if (a.iter_swap_invocations_) {
++(*a.iter_swap_invocations_);
}
}
friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr_ == rhs.ptr_; }
};
} // namespace adl
constexpr bool test() {
// Can use `iter_swap` with a regular array.
{
constexpr int N = 3;
int a[N] = {0, 1, 2};
std::reverse_iterator rb(a + N);
std::reverse_iterator re(a + 1);
assert(a[0] == 0);
assert(a[2] == 2);
static_assert(std::same_as<decltype(iter_swap(rb, re)), void>);
iter_swap(rb, re);
assert(a[0] == 2);
assert(a[2] == 0);
}
// Ensure the `iter_swap` customization point is being used.
{
int iter_swap_invocations = 0;
adl::Iterator i1(iter_swap_invocations), i2(iter_swap_invocations);
std::reverse_iterator<adl::Iterator> ri1(i1), ri2(i2);
iter_swap(i1, i2);
assert(iter_swap_invocations == 1);
iter_swap(i2, i1);
assert(iter_swap_invocations == 2);
}
// Check the `noexcept` specification.
{
{
struct ThrowingCopyNoexceptDecrement {
using value_type = int;
using difference_type = ptrdiff_t;
ThrowingCopyNoexceptDecrement();
ThrowingCopyNoexceptDecrement(const ThrowingCopyNoexceptDecrement&);
int& operator*() const noexcept { static int x; return x; }
ThrowingCopyNoexceptDecrement& operator++();
ThrowingCopyNoexceptDecrement operator++(int);
ThrowingCopyNoexceptDecrement& operator--() noexcept;
ThrowingCopyNoexceptDecrement operator--(int) noexcept;
bool operator==(const ThrowingCopyNoexceptDecrement&) const = default;
};
static_assert(std::bidirectional_iterator<ThrowingCopyNoexceptDecrement>);
static_assert(!std::is_nothrow_copy_constructible_v<ThrowingCopyNoexceptDecrement>);
static_assert( std::is_nothrow_copy_constructible_v<int*>);
ASSERT_NOEXCEPT(std::ranges::iter_swap(--std::declval<ThrowingCopyNoexceptDecrement&>(), --std::declval<int*&>()));
using RI1 = std::reverse_iterator<ThrowingCopyNoexceptDecrement>;
using RI2 = std::reverse_iterator<int*>;
ASSERT_NOT_NOEXCEPT(iter_swap(std::declval<RI1>(), std::declval<RI2>()));
ASSERT_NOT_NOEXCEPT(iter_swap(std::declval<RI2>(), std::declval<RI1>()));
}
{
struct NoexceptCopyThrowingDecrement {
using value_type = int;
using difference_type = ptrdiff_t;
NoexceptCopyThrowingDecrement();
NoexceptCopyThrowingDecrement(const NoexceptCopyThrowingDecrement&) noexcept;
int& operator*() const { static int x; return x; }
NoexceptCopyThrowingDecrement& operator++();
NoexceptCopyThrowingDecrement operator++(int);
NoexceptCopyThrowingDecrement& operator--();
NoexceptCopyThrowingDecrement operator--(int);
bool operator==(const NoexceptCopyThrowingDecrement&) const = default;
};
static_assert(std::bidirectional_iterator<NoexceptCopyThrowingDecrement>);
static_assert( std::is_nothrow_copy_constructible_v<NoexceptCopyThrowingDecrement>);
static_assert( std::is_nothrow_copy_constructible_v<int*>);
ASSERT_NOT_NOEXCEPT(std::ranges::iter_swap(--std::declval<NoexceptCopyThrowingDecrement&>(), --std::declval<int*&>()));
using RI1 = std::reverse_iterator<NoexceptCopyThrowingDecrement>;
using RI2 = std::reverse_iterator<int*>;
ASSERT_NOT_NOEXCEPT(iter_swap(std::declval<RI1>(), std::declval<RI2>()));
ASSERT_NOT_NOEXCEPT(iter_swap(std::declval<RI2>(), std::declval<RI1>()));
}
{
struct NoexceptCopyAndDecrement {
using value_type = int;
using difference_type = ptrdiff_t;
NoexceptCopyAndDecrement();
NoexceptCopyAndDecrement(const NoexceptCopyAndDecrement&) noexcept;
int& operator*() const noexcept { static int x; return x; }
NoexceptCopyAndDecrement& operator++();
NoexceptCopyAndDecrement operator++(int);
NoexceptCopyAndDecrement& operator--() noexcept;
NoexceptCopyAndDecrement operator--(int) noexcept;
bool operator==(const NoexceptCopyAndDecrement&) const = default;
};
static_assert(std::bidirectional_iterator<NoexceptCopyAndDecrement>);
static_assert( std::is_nothrow_copy_constructible_v<NoexceptCopyAndDecrement>);
static_assert( std::is_nothrow_copy_constructible_v<int*>);
ASSERT_NOEXCEPT(std::ranges::iter_swap(--std::declval<NoexceptCopyAndDecrement&>(), --std::declval<int*&>()));
using RI1 = std::reverse_iterator<NoexceptCopyAndDecrement>;
using RI2 = std::reverse_iterator<int*>;
ASSERT_NOEXCEPT(iter_swap(std::declval<RI1>(), std::declval<RI2>()));
ASSERT_NOEXCEPT(iter_swap(std::declval<RI2>(), std::declval<RI1>()));
}
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,29 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-no-concepts
// reverse_iterator
#include <iterator>
#include "test_iterators.h"
template<class T> concept HasMinus = requires (T t) { t - t; };
using sized_it = random_access_iterator<int*>;
static_assert( std::sized_sentinel_for<sized_it, sized_it>);
static_assert( std::sized_sentinel_for<std::reverse_iterator<sized_it>, std::reverse_iterator<sized_it>>);
static_assert( HasMinus<std::reverse_iterator<sized_it>>);
// Check that `sized_sentinel_for` is false for `reverse_iterator`s if it is false for the underlying iterators.
using unsized_it = bidirectional_iterator<int*>;
static_assert(!std::sized_sentinel_for<unsized_it, unsized_it>);
static_assert(!std::sized_sentinel_for<std::reverse_iterator<unsized_it>, std::reverse_iterator<unsized_it>>);
static_assert(!HasMinus<std::reverse_iterator<unsized_it>>);

View File

@ -0,0 +1,120 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// <iterator>
// reverse_iterator
// Test nested types and data member:
// template <BidirectionalIterator Iter>
// class reverse_iterator {
// protected:
// Iter current;
// public:
// iterator<typename iterator_traits<Iterator>::iterator_category,
// typename iterator_traits<Iterator>::value_type,
// typename iterator_traits<Iterator>::difference_type,
// typename iterator_traits<Iterator>::pointer,
// typename iterator_traits<Iterator>::reference> {
// };
#include <iterator>
#include <type_traits>
#include "test_macros.h"
#include "test_iterators.h"
template <class It>
struct find_current
: private std::reverse_iterator<It>
{
void test() { (void)this->current; }
};
template <class It>
void test() {
typedef std::reverse_iterator<It> R;
typedef std::iterator_traits<It> T;
find_current<It> q; q.test(); // Just test that we can access `.current` from derived classes
static_assert((std::is_same<typename R::iterator_type, It>::value), "");
static_assert((std::is_same<typename R::value_type, typename T::value_type>::value), "");
static_assert((std::is_same<typename R::difference_type, typename T::difference_type>::value), "");
static_assert((std::is_same<typename R::reference, typename T::reference>::value), "");
static_assert((std::is_same<typename R::pointer, typename std::iterator_traits<It>::pointer>::value), "");
#if TEST_STD_VER <= 14
typedef std::iterator<typename T::iterator_category, typename T::value_type> iterator_base;
static_assert((std::is_base_of<iterator_base, R>::value), "");
#endif
#if TEST_STD_VER > 17
if constexpr (std::is_same_v<typename T::iterator_category, std::contiguous_iterator_tag>) {
static_assert((std::is_same<typename R::iterator_category, std::random_access_iterator_tag>::value), "");
} else {
static_assert((std::is_same<typename R::iterator_category, typename T::iterator_category>::value), "");
}
#else
static_assert((std::is_same<typename R::iterator_category, typename T::iterator_category>::value), "");
#endif
}
#if TEST_STD_VER > 17
struct FooIter {
using iterator_category = std::bidirectional_iterator_tag;
using value_type = void*;
using difference_type = void*;
using pointer = void*;
using reference = int&;
int& operator*() const;
};
template <>
struct std::indirectly_readable_traits<FooIter> {
using value_type = int;
};
template <>
struct std::incrementable_traits<FooIter> {
using difference_type = char;
};
static_assert(std::is_same_v<typename std::reverse_iterator<FooIter>::value_type, int>);
static_assert(std::is_same_v<typename std::reverse_iterator<FooIter>::difference_type, char>);
#endif
struct BarIter {
bool& operator*() const;
};
template <>
struct std::iterator_traits<BarIter> {
using difference_type = char;
using value_type = char;
using pointer = char*;
using reference = char&;
using iterator_category = std::bidirectional_iterator_tag;
};
#if TEST_STD_VER > 17
static_assert(std::is_same_v<typename std::reverse_iterator<BarIter>::reference, bool&>);
#else
static_assert(std::is_same<typename std::reverse_iterator<BarIter>::reference, char&>::value, "");
#endif
void test_all() {
test<bidirectional_iterator<char*> >();
test<random_access_iterator<char*> >();
test<char*>();
#if TEST_STD_VER > 17
test<contiguous_iterator<char*>>();
static_assert(std::is_same_v<typename std::reverse_iterator<bidirectional_iterator<char*>>::iterator_concept, std::bidirectional_iterator_tag>);
static_assert(std::is_same_v<typename std::reverse_iterator<random_access_iterator<char*>>::iterator_concept, std::random_access_iterator_tag>);
static_assert(std::is_same_v<typename std::reverse_iterator<contiguous_iterator<char*>>::iterator_concept, std::random_access_iterator_tag>);
static_assert(std::is_same_v<typename std::reverse_iterator<char*>::iterator_concept, std::random_access_iterator_tag>);
#endif
}

View File

@ -1,83 +0,0 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// <iterator>
// reverse_iterator
// Test nested types and data member:
// template <BidirectionalIterator Iter>
// class reverse_iterator {
// protected:
// Iter current;
// public:
// iterator<typename iterator_traits<Iterator>::iterator_category,
// typename iterator_traits<Iterator>::value_type,
// typename iterator_traits<Iterator>::difference_type,
// typename iterator_traits<Iterator>::pointer,
// typename iterator_traits<Iterator>::reference> {
// };
#include <iterator>
#include <type_traits>
#include "test_macros.h"
#include "test_iterators.h"
template <class It>
struct find_current
: private std::reverse_iterator<It>
{
void test() { (void)this->current; }
};
template <class It>
void
test()
{
typedef std::reverse_iterator<It> R;
typedef std::iterator_traits<It> T;
find_current<It> q; q.test(); // Just test that we can access `.current` from derived classes
static_assert((std::is_same<typename R::iterator_type, It>::value), "");
static_assert((std::is_same<typename R::value_type, typename T::value_type>::value), "");
static_assert((std::is_same<typename R::difference_type, typename T::difference_type>::value), "");
static_assert((std::is_same<typename R::reference, typename T::reference>::value), "");
static_assert((std::is_same<typename R::pointer, typename std::iterator_traits<It>::pointer>::value), "");
#if TEST_STD_VER <= 14
typedef std::iterator<typename T::iterator_category, typename T::value_type> iterator_base;
static_assert((std::is_base_of<iterator_base, R>::value), "");
#endif
#if TEST_STD_VER > 17
if constexpr (std::is_same_v<typename T::iterator_category, std::contiguous_iterator_tag>) {
static_assert((std::is_same<typename R::iterator_category, std::random_access_iterator_tag>::value), "");
} else {
static_assert((std::is_same<typename R::iterator_category, typename T::iterator_category>::value), "");
}
#else
static_assert((std::is_same<typename R::iterator_category, typename T::iterator_category>::value), "");
#endif
}
int main(int, char**)
{
test<bidirectional_iterator<char*> >();
test<random_access_iterator<char*> >();
test<char*>();
#if TEST_STD_VER > 17
test<contiguous_iterator<char*>>();
static_assert(std::is_same_v<typename std::reverse_iterator<bidirectional_iterator<char*>>::iterator_concept, std::bidirectional_iterator_tag>);
static_assert(std::is_same_v<typename std::reverse_iterator<random_access_iterator<char*>>::iterator_concept, std::random_access_iterator_tag>);
static_assert(std::is_same_v<typename std::reverse_iterator<contiguous_iterator<char*>>::iterator_concept, std::random_access_iterator_tag>);
static_assert(std::is_same_v<typename std::reverse_iterator<char*>::iterator_concept, std::random_access_iterator_tag>);
#endif
return 0;
}