[libc++][test] Test flat_meow with proper underlying iterators (#131290)

Flat container adaptors require the iterators of underlying containers
to be random access, and it is required that random access container
iterators must support three-way comparison ([container.reqmts]/39 - /41).

As a result, we should at least avoid testing "containers" with random
access but not three-way comparable iterators for flat container
adaptors.

This patch adds a new class template `three_way_random_access_iterator`
to `test_iterators.h` and fixes some usages of `MinSequenceContainer`
with the new iterators.
This commit is contained in:
A. Jiang
2025-05-07 04:15:19 +08:00
committed by GitHub
parent b67880d875
commit 2f54a843bb
6 changed files with 220 additions and 101 deletions

View File

@@ -28,7 +28,6 @@ void test() {
using Key = typename KeyContainer::value_type;
using Value = typename ValueContainer::value_type;
using M = std::flat_map<Key, Value, std::less<Key>, KeyContainer, ValueContainer>;
using KI = typename KeyContainer::iterator;
using I = M::iterator;
using CI = M::const_iterator;
using RI = M::reverse_iterator;
@@ -115,34 +114,32 @@ void test() {
assert(cri2 >= cri2);
assert(!(cri1 >= cri2));
if constexpr (std::three_way_comparable<KI>) {
static_assert(std::three_way_comparable<I>); // ...of course the wrapped iterators still support <=>.
static_assert(std::three_way_comparable<CI>);
static_assert(std::three_way_comparable<RI>);
static_assert(std::three_way_comparable<CRI>);
static_assert(std::same_as<decltype(I() <=> I()), std::strong_ordering>);
static_assert(std::same_as<decltype(I() <=> CI()), std::strong_ordering>);
static_assert(std::same_as<decltype(CI() <=> CI()), std::strong_ordering>);
static_assert(std::same_as<decltype(RI() <=> RI()), std::strong_ordering>);
static_assert(std::same_as<decltype(RI() <=> CRI()), std::strong_ordering>);
static_assert(std::same_as<decltype(CRI() <=> CRI()), std::strong_ordering>);
static_assert(std::three_way_comparable<I>); // ...of course the wrapped iterators still support <=>.
static_assert(std::three_way_comparable<CI>);
static_assert(std::three_way_comparable<RI>);
static_assert(std::three_way_comparable<CRI>);
static_assert(std::same_as<decltype(I() <=> I()), std::strong_ordering>);
static_assert(std::same_as<decltype(I() <=> CI()), std::strong_ordering>);
static_assert(std::same_as<decltype(CI() <=> CI()), std::strong_ordering>);
static_assert(std::same_as<decltype(RI() <=> RI()), std::strong_ordering>);
static_assert(std::same_as<decltype(RI() <=> CRI()), std::strong_ordering>);
static_assert(std::same_as<decltype(CRI() <=> CRI()), std::strong_ordering>);
assert(i1 <=> i1 == std::strong_ordering::equivalent);
assert(i1 <=> i2 == std::strong_ordering::less);
assert(i2 <=> i1 == std::strong_ordering::greater);
assert(i1 <=> i1 == std::strong_ordering::equivalent);
assert(i1 <=> i2 == std::strong_ordering::less);
assert(i2 <=> i1 == std::strong_ordering::greater);
assert(ci1 <=> ci1 == std::strong_ordering::equivalent);
assert(ci1 <=> ci2 == std::strong_ordering::less);
assert(ci2 <=> ci1 == std::strong_ordering::greater);
assert(ci1 <=> ci1 == std::strong_ordering::equivalent);
assert(ci1 <=> ci2 == std::strong_ordering::less);
assert(ci2 <=> ci1 == std::strong_ordering::greater);
assert(ri1 <=> ri1 == std::strong_ordering::equivalent);
assert(ri1 <=> ri2 == std::strong_ordering::less);
assert(ri2 <=> ri1 == std::strong_ordering::greater);
assert(ri1 <=> ri1 == std::strong_ordering::equivalent);
assert(ri1 <=> ri2 == std::strong_ordering::less);
assert(ri2 <=> ri1 == std::strong_ordering::greater);
assert(cri1 <=> cri1 == std::strong_ordering::equivalent);
assert(cri1 <=> cri2 == std::strong_ordering::less);
assert(cri2 <=> cri1 == std::strong_ordering::greater);
}
assert(cri1 <=> cri1 == std::strong_ordering::equivalent);
assert(cri1 <=> cri2 == std::strong_ordering::less);
assert(cri2 <=> cri1 == std::strong_ordering::greater);
}
int main(int, char**) {

View File

@@ -28,7 +28,6 @@ void test() {
using Key = typename KeyContainer::value_type;
using Value = typename ValueContainer::value_type;
using M = std::flat_multimap<Key, Value, std::less<Key>, KeyContainer, ValueContainer>;
using KI = typename KeyContainer::iterator;
using I = M::iterator;
using CI = M::const_iterator;
using RI = M::reverse_iterator;
@@ -115,34 +114,32 @@ void test() {
assert(cri2 >= cri2);
assert(!(cri1 >= cri2));
if constexpr (std::three_way_comparable<KI>) {
static_assert(std::three_way_comparable<I>); // ...of course the wrapped iterators still support <=>.
static_assert(std::three_way_comparable<CI>);
static_assert(std::three_way_comparable<RI>);
static_assert(std::three_way_comparable<CRI>);
static_assert(std::same_as<decltype(I() <=> I()), std::strong_ordering>);
static_assert(std::same_as<decltype(I() <=> CI()), std::strong_ordering>);
static_assert(std::same_as<decltype(CI() <=> CI()), std::strong_ordering>);
static_assert(std::same_as<decltype(RI() <=> RI()), std::strong_ordering>);
static_assert(std::same_as<decltype(RI() <=> CRI()), std::strong_ordering>);
static_assert(std::same_as<decltype(CRI() <=> CRI()), std::strong_ordering>);
static_assert(std::three_way_comparable<I>); // ...of course the wrapped iterators still support <=>.
static_assert(std::three_way_comparable<CI>);
static_assert(std::three_way_comparable<RI>);
static_assert(std::three_way_comparable<CRI>);
static_assert(std::same_as<decltype(I() <=> I()), std::strong_ordering>);
static_assert(std::same_as<decltype(I() <=> CI()), std::strong_ordering>);
static_assert(std::same_as<decltype(CI() <=> CI()), std::strong_ordering>);
static_assert(std::same_as<decltype(RI() <=> RI()), std::strong_ordering>);
static_assert(std::same_as<decltype(RI() <=> CRI()), std::strong_ordering>);
static_assert(std::same_as<decltype(CRI() <=> CRI()), std::strong_ordering>);
assert(i1 <=> i1 == std::strong_ordering::equivalent);
assert(i1 <=> i2 == std::strong_ordering::less);
assert(i2 <=> i1 == std::strong_ordering::greater);
assert(i1 <=> i1 == std::strong_ordering::equivalent);
assert(i1 <=> i2 == std::strong_ordering::less);
assert(i2 <=> i1 == std::strong_ordering::greater);
assert(ci1 <=> ci1 == std::strong_ordering::equivalent);
assert(ci1 <=> ci2 == std::strong_ordering::less);
assert(ci2 <=> ci1 == std::strong_ordering::greater);
assert(ci1 <=> ci1 == std::strong_ordering::equivalent);
assert(ci1 <=> ci2 == std::strong_ordering::less);
assert(ci2 <=> ci1 == std::strong_ordering::greater);
assert(ri1 <=> ri1 == std::strong_ordering::equivalent);
assert(ri1 <=> ri2 == std::strong_ordering::less);
assert(ri2 <=> ri1 == std::strong_ordering::greater);
assert(ri1 <=> ri1 == std::strong_ordering::equivalent);
assert(ri1 <=> ri2 == std::strong_ordering::less);
assert(ri2 <=> ri1 == std::strong_ordering::greater);
assert(cri1 <=> cri1 == std::strong_ordering::equivalent);
assert(cri1 <=> cri2 == std::strong_ordering::less);
assert(cri2 <=> cri1 == std::strong_ordering::greater);
}
assert(cri1 <=> cri1 == std::strong_ordering::equivalent);
assert(cri1 <=> cri2 == std::strong_ordering::less);
assert(cri2 <=> cri1 == std::strong_ordering::greater);
}
int main(int, char**) {

View File

@@ -27,7 +27,6 @@ template <class KeyContainer>
void test_one() {
using Key = typename KeyContainer::value_type;
using M = std::flat_multiset<Key, std::less<Key>, KeyContainer>;
using KI = typename KeyContainer::iterator;
using I = M::iterator;
using CI = M::const_iterator;
using RI = M::reverse_iterator;
@@ -114,34 +113,32 @@ void test_one() {
assert(cri2 >= cri2);
assert(!(cri1 >= cri2));
if constexpr (std::three_way_comparable<KI>) {
static_assert(std::three_way_comparable<I>); // ...of course the wrapped iterators still support <=>.
static_assert(std::three_way_comparable<CI>);
static_assert(std::three_way_comparable<RI>);
static_assert(std::three_way_comparable<CRI>);
static_assert(std::same_as<decltype(I() <=> I()), std::strong_ordering>);
static_assert(std::same_as<decltype(I() <=> CI()), std::strong_ordering>);
static_assert(std::same_as<decltype(CI() <=> CI()), std::strong_ordering>);
static_assert(std::same_as<decltype(RI() <=> RI()), std::strong_ordering>);
static_assert(std::same_as<decltype(RI() <=> CRI()), std::strong_ordering>);
static_assert(std::same_as<decltype(CRI() <=> CRI()), std::strong_ordering>);
static_assert(std::three_way_comparable<I>); // ...of course the wrapped iterators still support <=>.
static_assert(std::three_way_comparable<CI>);
static_assert(std::three_way_comparable<RI>);
static_assert(std::three_way_comparable<CRI>);
static_assert(std::same_as<decltype(I() <=> I()), std::strong_ordering>);
static_assert(std::same_as<decltype(I() <=> CI()), std::strong_ordering>);
static_assert(std::same_as<decltype(CI() <=> CI()), std::strong_ordering>);
static_assert(std::same_as<decltype(RI() <=> RI()), std::strong_ordering>);
static_assert(std::same_as<decltype(RI() <=> CRI()), std::strong_ordering>);
static_assert(std::same_as<decltype(CRI() <=> CRI()), std::strong_ordering>);
assert(i1 <=> i1 == std::strong_ordering::equivalent);
assert(i1 <=> i2 == std::strong_ordering::less);
assert(i2 <=> i1 == std::strong_ordering::greater);
assert(i1 <=> i1 == std::strong_ordering::equivalent);
assert(i1 <=> i2 == std::strong_ordering::less);
assert(i2 <=> i1 == std::strong_ordering::greater);
assert(ci1 <=> ci1 == std::strong_ordering::equivalent);
assert(ci1 <=> ci2 == std::strong_ordering::less);
assert(ci2 <=> ci1 == std::strong_ordering::greater);
assert(ci1 <=> ci1 == std::strong_ordering::equivalent);
assert(ci1 <=> ci2 == std::strong_ordering::less);
assert(ci2 <=> ci1 == std::strong_ordering::greater);
assert(ri1 <=> ri1 == std::strong_ordering::equivalent);
assert(ri1 <=> ri2 == std::strong_ordering::less);
assert(ri2 <=> ri1 == std::strong_ordering::greater);
assert(ri1 <=> ri1 == std::strong_ordering::equivalent);
assert(ri1 <=> ri2 == std::strong_ordering::less);
assert(ri2 <=> ri1 == std::strong_ordering::greater);
assert(cri1 <=> cri1 == std::strong_ordering::equivalent);
assert(cri1 <=> cri2 == std::strong_ordering::less);
assert(cri2 <=> cri1 == std::strong_ordering::greater);
}
assert(cri1 <=> cri1 == std::strong_ordering::equivalent);
assert(cri1 <=> cri2 == std::strong_ordering::less);
assert(cri2 <=> cri1 == std::strong_ordering::greater);
}
void test() {

View File

@@ -27,7 +27,6 @@ template <class KeyContainer>
void test_one() {
using Key = typename KeyContainer::value_type;
using M = std::flat_set<Key, std::less<Key>, KeyContainer>;
using KI = typename KeyContainer::iterator;
using I = M::iterator;
using CI = M::const_iterator;
using RI = M::reverse_iterator;
@@ -114,34 +113,32 @@ void test_one() {
assert(cri2 >= cri2);
assert(!(cri1 >= cri2));
if constexpr (std::three_way_comparable<KI>) {
static_assert(std::three_way_comparable<I>); // ...of course the wrapped iterators still support <=>.
static_assert(std::three_way_comparable<CI>);
static_assert(std::three_way_comparable<RI>);
static_assert(std::three_way_comparable<CRI>);
static_assert(std::same_as<decltype(I() <=> I()), std::strong_ordering>);
static_assert(std::same_as<decltype(I() <=> CI()), std::strong_ordering>);
static_assert(std::same_as<decltype(CI() <=> CI()), std::strong_ordering>);
static_assert(std::same_as<decltype(RI() <=> RI()), std::strong_ordering>);
static_assert(std::same_as<decltype(RI() <=> CRI()), std::strong_ordering>);
static_assert(std::same_as<decltype(CRI() <=> CRI()), std::strong_ordering>);
static_assert(std::three_way_comparable<I>); // ...of course the wrapped iterators still support <=>.
static_assert(std::three_way_comparable<CI>);
static_assert(std::three_way_comparable<RI>);
static_assert(std::three_way_comparable<CRI>);
static_assert(std::same_as<decltype(I() <=> I()), std::strong_ordering>);
static_assert(std::same_as<decltype(I() <=> CI()), std::strong_ordering>);
static_assert(std::same_as<decltype(CI() <=> CI()), std::strong_ordering>);
static_assert(std::same_as<decltype(RI() <=> RI()), std::strong_ordering>);
static_assert(std::same_as<decltype(RI() <=> CRI()), std::strong_ordering>);
static_assert(std::same_as<decltype(CRI() <=> CRI()), std::strong_ordering>);
assert(i1 <=> i1 == std::strong_ordering::equivalent);
assert(i1 <=> i2 == std::strong_ordering::less);
assert(i2 <=> i1 == std::strong_ordering::greater);
assert(i1 <=> i1 == std::strong_ordering::equivalent);
assert(i1 <=> i2 == std::strong_ordering::less);
assert(i2 <=> i1 == std::strong_ordering::greater);
assert(ci1 <=> ci1 == std::strong_ordering::equivalent);
assert(ci1 <=> ci2 == std::strong_ordering::less);
assert(ci2 <=> ci1 == std::strong_ordering::greater);
assert(ci1 <=> ci1 == std::strong_ordering::equivalent);
assert(ci1 <=> ci2 == std::strong_ordering::less);
assert(ci2 <=> ci1 == std::strong_ordering::greater);
assert(ri1 <=> ri1 == std::strong_ordering::equivalent);
assert(ri1 <=> ri2 == std::strong_ordering::less);
assert(ri2 <=> ri1 == std::strong_ordering::greater);
assert(ri1 <=> ri1 == std::strong_ordering::equivalent);
assert(ri1 <=> ri2 == std::strong_ordering::less);
assert(ri2 <=> ri1 == std::strong_ordering::greater);
assert(cri1 <=> cri1 == std::strong_ordering::equivalent);
assert(cri1 <=> cri2 == std::strong_ordering::less);
assert(cri2 <=> cri1 == std::strong_ordering::greater);
}
assert(cri1 <=> cri1 == std::strong_ordering::equivalent);
assert(cri1 <=> cri2 == std::strong_ordering::less);
assert(cri2 <=> cri1 == std::strong_ordering::greater);
}
void test() {

View File

@@ -14,7 +14,9 @@
#include "test_iterators.h"
template <class T, class Iterator = random_access_iterator<T*>, class ConstIterator = random_access_iterator<const T*>>
template <class T,
class Iterator = three_way_random_access_iterator<T*>,
class ConstIterator = three_way_random_access_iterator<const T*>>
struct MinSequenceContainer {
using value_type = T;
using difference_type = int;

View File

@@ -267,6 +267,135 @@ template <class It>
random_access_iterator(It) -> random_access_iterator<It>;
#endif
// Since C++20, a container iterator type that is random access is also required to support three-way comparison.
// See C++20 [tab:container.req], C++23 [container.reqmts]/39 - /41.
template <class It>
class three_way_random_access_iterator {
It it_;
support::double_move_tracker tracker_;
template <class U>
friend class three_way_random_access_iterator;
public:
typedef std::random_access_iterator_tag iterator_category;
typedef typename std::iterator_traits<It>::value_type value_type;
typedef typename std::iterator_traits<It>::difference_type difference_type;
typedef It pointer;
typedef typename std::iterator_traits<It>::reference reference;
TEST_CONSTEXPR three_way_random_access_iterator() : it_() {}
TEST_CONSTEXPR explicit three_way_random_access_iterator(It it) : it_(it) {}
template <class U>
TEST_CONSTEXPR three_way_random_access_iterator(const three_way_random_access_iterator<U>& u)
: it_(u.it_), tracker_(u.tracker_) {}
template <class U, class = typename std::enable_if<std::is_default_constructible<U>::value>::type>
TEST_CONSTEXPR_CXX14 three_way_random_access_iterator(three_way_random_access_iterator<U>&& u)
: it_(std::move(u.it_)), tracker_(std::move(u.tracker_)) {
u.it_ = U();
}
TEST_CONSTEXPR_CXX14 reference operator*() const { return *it_; }
TEST_CONSTEXPR_CXX14 reference operator[](difference_type n) const { return it_[n]; }
TEST_CONSTEXPR_CXX14 three_way_random_access_iterator& operator++() {
++it_;
return *this;
}
TEST_CONSTEXPR_CXX14 three_way_random_access_iterator& operator--() {
--it_;
return *this;
}
TEST_CONSTEXPR_CXX14 three_way_random_access_iterator operator++(int) {
return three_way_random_access_iterator(it_++);
}
TEST_CONSTEXPR_CXX14 three_way_random_access_iterator operator--(int) {
return three_way_random_access_iterator(it_--);
}
TEST_CONSTEXPR_CXX14 three_way_random_access_iterator& operator+=(difference_type n) {
it_ += n;
return *this;
}
TEST_CONSTEXPR_CXX14 three_way_random_access_iterator& operator-=(difference_type n) {
it_ -= n;
return *this;
}
friend TEST_CONSTEXPR_CXX14 three_way_random_access_iterator
operator+(three_way_random_access_iterator x, difference_type n) {
x += n;
return x;
}
friend TEST_CONSTEXPR_CXX14 three_way_random_access_iterator
operator+(difference_type n, three_way_random_access_iterator x) {
x += n;
return x;
}
friend TEST_CONSTEXPR_CXX14 three_way_random_access_iterator
operator-(three_way_random_access_iterator x, difference_type n) {
x -= n;
return x;
}
friend TEST_CONSTEXPR difference_type
operator-(three_way_random_access_iterator x, three_way_random_access_iterator y) {
return x.it_ - y.it_;
}
friend TEST_CONSTEXPR bool
operator==(const three_way_random_access_iterator& x, const three_way_random_access_iterator& y) {
return x.it_ == y.it_;
}
#if TEST_STD_VER < 20
friend TEST_CONSTEXPR bool
operator!=(const three_way_random_access_iterator& x, const three_way_random_access_iterator& y) {
return x.it_ != y.it_;
}
#endif
friend TEST_CONSTEXPR bool
operator<(const three_way_random_access_iterator& x, const three_way_random_access_iterator& y) {
return x.it_ < y.it_;
}
friend TEST_CONSTEXPR bool
operator<=(const three_way_random_access_iterator& x, const three_way_random_access_iterator& y) {
return x.it_ <= y.it_;
}
friend TEST_CONSTEXPR bool
operator>(const three_way_random_access_iterator& x, const three_way_random_access_iterator& y) {
return x.it_ > y.it_;
}
friend TEST_CONSTEXPR bool
operator>=(const three_way_random_access_iterator& x, const three_way_random_access_iterator& y) {
return x.it_ >= y.it_;
}
#if TEST_STD_VER >= 20
friend constexpr std::strong_ordering
operator<=>(const three_way_random_access_iterator& x, const three_way_random_access_iterator& y) {
if constexpr (std::three_way_comparable<It>) {
return x.it_ <=> y.it_;
} else {
if (x.it_ < y.it_) {
return std::strong_ordering::less;
} else if (y.it_ < x.it_) {
return std::strong_ordering::greater;
} else {
return std::strong_ordering::equal;
}
}
}
#endif
friend TEST_CONSTEXPR It base(const three_way_random_access_iterator& i) { return i.it_; }
template <class T>
void operator,(T const&) = delete;
};
#if TEST_STD_VER > 14
template <class It>
three_way_random_access_iterator(It) -> three_way_random_access_iterator<It>;
#endif
#if TEST_STD_VER > 17
template <std::random_access_iterator It>