Skip to content

Commit d03328e

Browse files
[libc++] Make flat_(multi)map's iterators require operator<=>
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 can and perhaps should reject containers with unexpected iterator types, as they're invalid since C++20.
1 parent f2e1027 commit d03328e

File tree

7 files changed

+346
-60
lines changed

7 files changed

+346
-60
lines changed

libcxx/include/__flat_map/key_value_iterator.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010
#ifndef _LIBCPP___FLAT_MAP_KEY_VALUE_ITERATOR_H
1111
#define _LIBCPP___FLAT_MAP_KEY_VALUE_ITERATOR_H
1212

13+
#include <__compare/ordering.h>
1314
#include <__compare/three_way_comparable.h>
1415
#include <__concepts/convertible_to.h>
1516
#include <__config>
1617
#include <__iterator/iterator_traits.h>
1718
#include <__memory/addressof.h>
1819
#include <__type_traits/conditional.h>
20+
#include <__type_traits/is_same.h>
1921
#include <__utility/move.h>
2022
#include <__utility/pair.h>
2123

@@ -139,9 +141,12 @@ struct __key_value_iterator {
139141
return !(__x < __y);
140142
}
141143

142-
_LIBCPP_HIDE_FROM_ABI friend auto operator<=>(const __key_value_iterator& __x, const __key_value_iterator& __y)
143-
requires three_way_comparable<__key_iterator>
144-
{
144+
_LIBCPP_HIDE_FROM_ABI friend strong_ordering
145+
operator<=>(const __key_value_iterator& __x, const __key_value_iterator& __y) {
146+
static_assert(three_way_comparable<__key_iterator>,
147+
"random accesss iterator not supporting three-way comparison is invalid for container");
148+
static_assert(is_same_v<decltype(__x.__key_iter_ <=> __y.__key_iter_), strong_ordering>,
149+
"three-way comparison between random accesss container iterators must return std::strong_ordering");
145150
return __x.__key_iter_ <=> __y.__key_iter_;
146151
}
147152

libcxx/test/std/containers/container.adaptors/flat.map/flat.map.iterators/iterator_comparison.pass.cpp

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -115,34 +115,32 @@ void test() {
115115
assert(cri2 >= cri2);
116116
assert(!(cri1 >= cri2));
117117

118-
if constexpr (std::three_way_comparable<KI>) {
119-
static_assert(std::three_way_comparable<I>); // ...of course the wrapped iterators still support <=>.
120-
static_assert(std::three_way_comparable<CI>);
121-
static_assert(std::three_way_comparable<RI>);
122-
static_assert(std::three_way_comparable<CRI>);
123-
static_assert(std::same_as<decltype(I() <=> I()), std::strong_ordering>);
124-
static_assert(std::same_as<decltype(I() <=> CI()), std::strong_ordering>);
125-
static_assert(std::same_as<decltype(CI() <=> CI()), std::strong_ordering>);
126-
static_assert(std::same_as<decltype(RI() <=> RI()), std::strong_ordering>);
127-
static_assert(std::same_as<decltype(RI() <=> CRI()), std::strong_ordering>);
128-
static_assert(std::same_as<decltype(CRI() <=> CRI()), std::strong_ordering>);
129-
130-
assert(i1 <=> i1 == std::strong_ordering::equivalent);
131-
assert(i1 <=> i2 == std::strong_ordering::less);
132-
assert(i2 <=> i1 == std::strong_ordering::greater);
133-
134-
assert(ci1 <=> ci1 == std::strong_ordering::equivalent);
135-
assert(ci1 <=> ci2 == std::strong_ordering::less);
136-
assert(ci2 <=> ci1 == std::strong_ordering::greater);
137-
138-
assert(ri1 <=> ri1 == std::strong_ordering::equivalent);
139-
assert(ri1 <=> ri2 == std::strong_ordering::less);
140-
assert(ri2 <=> ri1 == std::strong_ordering::greater);
141-
142-
assert(cri1 <=> cri1 == std::strong_ordering::equivalent);
143-
assert(cri1 <=> cri2 == std::strong_ordering::less);
144-
assert(cri2 <=> cri1 == std::strong_ordering::greater);
145-
}
118+
static_assert(std::three_way_comparable<I>); // ...of course the wrapped iterators still support <=>.
119+
static_assert(std::three_way_comparable<CI>);
120+
static_assert(std::three_way_comparable<RI>);
121+
static_assert(std::three_way_comparable<CRI>);
122+
static_assert(std::same_as<decltype(I() <=> I()), std::strong_ordering>);
123+
static_assert(std::same_as<decltype(I() <=> CI()), std::strong_ordering>);
124+
static_assert(std::same_as<decltype(CI() <=> CI()), std::strong_ordering>);
125+
static_assert(std::same_as<decltype(RI() <=> RI()), std::strong_ordering>);
126+
static_assert(std::same_as<decltype(RI() <=> CRI()), std::strong_ordering>);
127+
static_assert(std::same_as<decltype(CRI() <=> CRI()), std::strong_ordering>);
128+
129+
assert(i1 <=> i1 == std::strong_ordering::equivalent);
130+
assert(i1 <=> i2 == std::strong_ordering::less);
131+
assert(i2 <=> i1 == std::strong_ordering::greater);
132+
133+
assert(ci1 <=> ci1 == std::strong_ordering::equivalent);
134+
assert(ci1 <=> ci2 == std::strong_ordering::less);
135+
assert(ci2 <=> ci1 == std::strong_ordering::greater);
136+
137+
assert(ri1 <=> ri1 == std::strong_ordering::equivalent);
138+
assert(ri1 <=> ri2 == std::strong_ordering::less);
139+
assert(ri2 <=> ri1 == std::strong_ordering::greater);
140+
141+
assert(cri1 <=> cri1 == std::strong_ordering::equivalent);
142+
assert(cri1 <=> cri2 == std::strong_ordering::less);
143+
assert(cri2 <=> cri1 == std::strong_ordering::greater);
146144
}
147145

148146
int main(int, char**) {
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// REQUIRES: std-at-least-c++23
10+
11+
// <flat_map>
12+
13+
// For underlying iterators i and j, i <=> j must be well-formed and return std::strong_ordering.
14+
15+
#include <iterator>
16+
#include <type_traits>
17+
18+
#include "MinSequenceContainer.h"
19+
#include "test_iterators.h"
20+
21+
template <class It>
22+
class bad_3way_random_access_iterator {
23+
template <class U>
24+
friend class bad_3way_random_access_iterator;
25+
26+
public:
27+
typedef std::random_access_iterator_tag iterator_category;
28+
typedef typename std::iterator_traits<It>::value_type value_type;
29+
typedef typename std::iterator_traits<It>::difference_type difference_type;
30+
typedef It pointer;
31+
typedef typename std::iterator_traits<It>::reference reference;
32+
33+
bad_3way_random_access_iterator();
34+
explicit bad_3way_random_access_iterator(It);
35+
36+
template <class U>
37+
bad_3way_random_access_iterator(const bad_3way_random_access_iterator<U>& u);
38+
39+
template <class U, class = typename std::enable_if<std::is_default_constructible<U>::value>::type>
40+
TEST_CONSTEXPR_CXX14 bad_3way_random_access_iterator(bad_3way_random_access_iterator<U>&& u);
41+
42+
reference operator*() const;
43+
reference operator[](difference_type) const;
44+
45+
bad_3way_random_access_iterator& operator++();
46+
bad_3way_random_access_iterator& operator--();
47+
bad_3way_random_access_iterator operator++(int);
48+
bad_3way_random_access_iterator operator--(int);
49+
50+
bad_3way_random_access_iterator& operator+=(difference_type);
51+
bad_3way_random_access_iterator& operator-=(difference_type);
52+
friend bad_3way_random_access_iterator operator+(bad_3way_random_access_iterator, difference_type);
53+
friend bad_3way_random_access_iterator operator+(difference_type, bad_3way_random_access_iterator);
54+
friend bad_3way_random_access_iterator operator-(bad_3way_random_access_iterator, difference_type);
55+
friend difference_type operator-(bad_3way_random_access_iterator, bad_3way_random_access_iterator);
56+
57+
friend bool operator==(const bad_3way_random_access_iterator&, const bad_3way_random_access_iterator&);
58+
friend std::weak_ordering operator<=>(const bad_3way_random_access_iterator&, const bad_3way_random_access_iterator&);
59+
};
60+
61+
void test() {
62+
{
63+
using KeyCont = MinSequenceContainer<int, random_access_iterator<int*>, random_access_iterator<const int*>>;
64+
using FMap = std::flat_map<KeyCont, MinSequenceContainer<int>>;
65+
FMap m;
66+
// expected-error@*:* {{static assertion failed: random accesss iterator not supporting three-way comparison is invalid for container}}
67+
(void)(m.begin() <=> m.begin());
68+
}
69+
{
70+
using KeyCont =
71+
MinSequenceContainer<int, bad_3way_random_access_iterator<int*>, bad_3way_random_access_iterator<const int*>>;
72+
using FMap = std::flat_map<KeyCont, MinSequenceContainer<int>>;
73+
FMap m;
74+
// expected-error@*:* {{static assertion failed: three-way comparison between random accesss container iterators must return std::strong_ordering}}
75+
(void)(m.begin() <=> m.begin());
76+
}
77+
}

libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.iterators/iterator_comparison.pass.cpp

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -115,34 +115,32 @@ void test() {
115115
assert(cri2 >= cri2);
116116
assert(!(cri1 >= cri2));
117117

118-
if constexpr (std::three_way_comparable<KI>) {
119-
static_assert(std::three_way_comparable<I>); // ...of course the wrapped iterators still support <=>.
120-
static_assert(std::three_way_comparable<CI>);
121-
static_assert(std::three_way_comparable<RI>);
122-
static_assert(std::three_way_comparable<CRI>);
123-
static_assert(std::same_as<decltype(I() <=> I()), std::strong_ordering>);
124-
static_assert(std::same_as<decltype(I() <=> CI()), std::strong_ordering>);
125-
static_assert(std::same_as<decltype(CI() <=> CI()), std::strong_ordering>);
126-
static_assert(std::same_as<decltype(RI() <=> RI()), std::strong_ordering>);
127-
static_assert(std::same_as<decltype(RI() <=> CRI()), std::strong_ordering>);
128-
static_assert(std::same_as<decltype(CRI() <=> CRI()), std::strong_ordering>);
129-
130-
assert(i1 <=> i1 == std::strong_ordering::equivalent);
131-
assert(i1 <=> i2 == std::strong_ordering::less);
132-
assert(i2 <=> i1 == std::strong_ordering::greater);
133-
134-
assert(ci1 <=> ci1 == std::strong_ordering::equivalent);
135-
assert(ci1 <=> ci2 == std::strong_ordering::less);
136-
assert(ci2 <=> ci1 == std::strong_ordering::greater);
137-
138-
assert(ri1 <=> ri1 == std::strong_ordering::equivalent);
139-
assert(ri1 <=> ri2 == std::strong_ordering::less);
140-
assert(ri2 <=> ri1 == std::strong_ordering::greater);
141-
142-
assert(cri1 <=> cri1 == std::strong_ordering::equivalent);
143-
assert(cri1 <=> cri2 == std::strong_ordering::less);
144-
assert(cri2 <=> cri1 == std::strong_ordering::greater);
145-
}
118+
static_assert(std::three_way_comparable<I>); // ...of course the wrapped iterators still support <=>.
119+
static_assert(std::three_way_comparable<CI>);
120+
static_assert(std::three_way_comparable<RI>);
121+
static_assert(std::three_way_comparable<CRI>);
122+
static_assert(std::same_as<decltype(I() <=> I()), std::strong_ordering>);
123+
static_assert(std::same_as<decltype(I() <=> CI()), std::strong_ordering>);
124+
static_assert(std::same_as<decltype(CI() <=> CI()), std::strong_ordering>);
125+
static_assert(std::same_as<decltype(RI() <=> RI()), std::strong_ordering>);
126+
static_assert(std::same_as<decltype(RI() <=> CRI()), std::strong_ordering>);
127+
static_assert(std::same_as<decltype(CRI() <=> CRI()), std::strong_ordering>);
128+
129+
assert(i1 <=> i1 == std::strong_ordering::equivalent);
130+
assert(i1 <=> i2 == std::strong_ordering::less);
131+
assert(i2 <=> i1 == std::strong_ordering::greater);
132+
133+
assert(ci1 <=> ci1 == std::strong_ordering::equivalent);
134+
assert(ci1 <=> ci2 == std::strong_ordering::less);
135+
assert(ci2 <=> ci1 == std::strong_ordering::greater);
136+
137+
assert(ri1 <=> ri1 == std::strong_ordering::equivalent);
138+
assert(ri1 <=> ri2 == std::strong_ordering::less);
139+
assert(ri2 <=> ri1 == std::strong_ordering::greater);
140+
141+
assert(cri1 <=> cri1 == std::strong_ordering::equivalent);
142+
assert(cri1 <=> cri2 == std::strong_ordering::less);
143+
assert(cri2 <=> cri1 == std::strong_ordering::greater);
146144
}
147145

148146
int main(int, char**) {
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// REQUIRES: std-at-least-c++23
10+
11+
// <flat_map>
12+
13+
// For underlying iterators i and j, i <=> j must be well-formed and return std::strong_ordering.
14+
15+
#include <iterator>
16+
#include <type_traits>
17+
18+
#include "MinSequenceContainer.h"
19+
#include "test_iterators.h"
20+
21+
template <class It>
22+
class bad_3way_random_access_iterator {
23+
template <class U>
24+
friend class bad_3way_random_access_iterator;
25+
26+
public:
27+
typedef std::random_access_iterator_tag iterator_category;
28+
typedef typename std::iterator_traits<It>::value_type value_type;
29+
typedef typename std::iterator_traits<It>::difference_type difference_type;
30+
typedef It pointer;
31+
typedef typename std::iterator_traits<It>::reference reference;
32+
33+
bad_3way_random_access_iterator();
34+
explicit bad_3way_random_access_iterator(It);
35+
36+
template <class U>
37+
bad_3way_random_access_iterator(const bad_3way_random_access_iterator<U>& u);
38+
39+
template <class U, class = typename std::enable_if<std::is_default_constructible<U>::value>::type>
40+
TEST_CONSTEXPR_CXX14 bad_3way_random_access_iterator(bad_3way_random_access_iterator<U>&& u);
41+
42+
reference operator*() const;
43+
reference operator[](difference_type) const;
44+
45+
bad_3way_random_access_iterator& operator++();
46+
bad_3way_random_access_iterator& operator--();
47+
bad_3way_random_access_iterator operator++(int);
48+
bad_3way_random_access_iterator operator--(int);
49+
50+
bad_3way_random_access_iterator& operator+=(difference_type);
51+
bad_3way_random_access_iterator& operator-=(difference_type);
52+
friend bad_3way_random_access_iterator operator+(bad_3way_random_access_iterator, difference_type);
53+
friend bad_3way_random_access_iterator operator+(difference_type, bad_3way_random_access_iterator);
54+
friend bad_3way_random_access_iterator operator-(bad_3way_random_access_iterator, difference_type);
55+
friend difference_type operator-(bad_3way_random_access_iterator, bad_3way_random_access_iterator);
56+
57+
friend bool operator==(const bad_3way_random_access_iterator&, const bad_3way_random_access_iterator&);
58+
friend std::weak_ordering operator<=>(const bad_3way_random_access_iterator&, const bad_3way_random_access_iterator&);
59+
};
60+
61+
void test() {
62+
{
63+
using KeyCont = MinSequenceContainer<int, random_access_iterator<int*>, random_access_iterator<const int*>>;
64+
using FMap = std::flat_multimap<KeyCont, MinSequenceContainer<int>>;
65+
FMap m;
66+
// expected-error@*:* {{static assertion failed: random accesss iterator not supporting three-way comparison is invalid for container}}
67+
(void)(m.begin() <=> m.begin());
68+
}
69+
{
70+
using KeyCont =
71+
MinSequenceContainer<int, bad_3way_random_access_iterator<int*>, bad_3way_random_access_iterator<const int*>>;
72+
using FMap = std::flat_multimap<KeyCont, MinSequenceContainer<int>>;
73+
FMap m;
74+
// expected-error@*:* {{static assertion failed: three-way comparison between random accesss container iterators must return std::strong_ordering}}
75+
(void)(m.begin() <=> m.begin());
76+
}
77+
}

libcxx/test/support/MinSequenceContainer.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414

1515
#include "test_iterators.h"
1616

17-
template <class T, class Iterator = random_access_iterator<T*>, class ConstIterator = random_access_iterator<const T*>>
17+
template <class T,
18+
class Iterator = three_way_random_access_iterator<T*>,
19+
class ConstIterator = three_way_random_access_iterator<const T*>>
1820
struct MinSequenceContainer {
1921
using value_type = T;
2022
using difference_type = int;

0 commit comments

Comments
 (0)