Skip to content

Commit a967f96

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 3f62718 commit a967f96

File tree

7 files changed

+354
-62
lines changed

7 files changed

+354
-62
lines changed

libcxx/include/__flat_map/key_value_iterator.h

+8-3
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

+26-29
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ void test() {
2828
using Key = typename KeyContainer::value_type;
2929
using Value = typename ValueContainer::value_type;
3030
using M = std::flat_map<Key, Value, std::less<Key>, KeyContainer, ValueContainer>;
31-
using KI = typename KeyContainer::iterator;
3231
using I = M::iterator;
3332
using CI = M::const_iterator;
3433
using RI = M::reverse_iterator;
@@ -115,34 +114,32 @@ void test() {
115114
assert(cri2 >= cri2);
116115
assert(!(cri1 >= cri2));
117116

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-
}
117+
static_assert(std::three_way_comparable<I>); // ...of course the wrapped iterators still support <=>.
118+
static_assert(std::three_way_comparable<CI>);
119+
static_assert(std::three_way_comparable<RI>);
120+
static_assert(std::three_way_comparable<CRI>);
121+
static_assert(std::same_as<decltype(I() <=> I()), std::strong_ordering>);
122+
static_assert(std::same_as<decltype(I() <=> CI()), std::strong_ordering>);
123+
static_assert(std::same_as<decltype(CI() <=> CI()), std::strong_ordering>);
124+
static_assert(std::same_as<decltype(RI() <=> RI()), std::strong_ordering>);
125+
static_assert(std::same_as<decltype(RI() <=> CRI()), std::strong_ordering>);
126+
static_assert(std::same_as<decltype(CRI() <=> CRI()), std::strong_ordering>);
127+
128+
assert(i1 <=> i1 == std::strong_ordering::equivalent);
129+
assert(i1 <=> i2 == std::strong_ordering::less);
130+
assert(i2 <=> i1 == std::strong_ordering::greater);
131+
132+
assert(ci1 <=> ci1 == std::strong_ordering::equivalent);
133+
assert(ci1 <=> ci2 == std::strong_ordering::less);
134+
assert(ci2 <=> ci1 == std::strong_ordering::greater);
135+
136+
assert(ri1 <=> ri1 == std::strong_ordering::equivalent);
137+
assert(ri1 <=> ri2 == std::strong_ordering::less);
138+
assert(ri2 <=> ri1 == std::strong_ordering::greater);
139+
140+
assert(cri1 <=> cri1 == std::strong_ordering::equivalent);
141+
assert(cri1 <=> cri2 == std::strong_ordering::less);
142+
assert(cri2 <=> cri1 == std::strong_ordering::greater);
146143
}
147144

148145
int main(int, char**) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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 <flat_map>
16+
#include <functional>
17+
#include <iterator>
18+
#include <type_traits>
19+
20+
#include "MinSequenceContainer.h"
21+
#include "test_iterators.h"
22+
23+
template <class It>
24+
class bad_3way_random_access_iterator {
25+
template <class U>
26+
friend class bad_3way_random_access_iterator;
27+
28+
public:
29+
using iterator_category = std::random_access_iterator_tag;
30+
using value_type = typename std::iterator_traits<It>::value_type;
31+
using difference_type = typename std::iterator_traits<It>::difference_type;
32+
using pointer = It;
33+
using reference = typename std::iterator_traits<It>::reference;
34+
35+
bad_3way_random_access_iterator();
36+
explicit bad_3way_random_access_iterator(It);
37+
38+
template <class U>
39+
bad_3way_random_access_iterator(const bad_3way_random_access_iterator<U>&);
40+
41+
template <class U, class = typename std::enable_if<std::is_default_constructible<U>::value>::type>
42+
bad_3way_random_access_iterator(bad_3way_random_access_iterator<U>&&);
43+
44+
reference operator*() const;
45+
reference operator[](difference_type) const;
46+
47+
bad_3way_random_access_iterator& operator++();
48+
bad_3way_random_access_iterator& operator--();
49+
bad_3way_random_access_iterator operator++(int);
50+
bad_3way_random_access_iterator operator--(int);
51+
52+
bad_3way_random_access_iterator& operator+=(difference_type);
53+
bad_3way_random_access_iterator& operator-=(difference_type);
54+
friend bad_3way_random_access_iterator operator+(bad_3way_random_access_iterator, difference_type);
55+
friend bad_3way_random_access_iterator operator+(difference_type, bad_3way_random_access_iterator);
56+
friend bad_3way_random_access_iterator operator-(bad_3way_random_access_iterator, difference_type);
57+
friend difference_type operator-(bad_3way_random_access_iterator, bad_3way_random_access_iterator);
58+
59+
friend bool operator==(const bad_3way_random_access_iterator&, const bad_3way_random_access_iterator&);
60+
friend std::weak_ordering operator<=>(const bad_3way_random_access_iterator&, const bad_3way_random_access_iterator&);
61+
};
62+
63+
void test() {
64+
{
65+
using KeyCont = MinSequenceContainer<int, random_access_iterator<int*>, random_access_iterator<const int*>>;
66+
using FMap = std::flat_map<int, int, std::less<int>, KeyCont, MinSequenceContainer<int>>;
67+
FMap m;
68+
// expected-error@*:* {{static assertion failed: random accesss iterator not supporting three-way comparison is invalid for container}}
69+
// expected-error@*:* {{invalid operands to binary expression}}
70+
(void)(m.begin() <=> m.begin());
71+
}
72+
{
73+
using KeyCont =
74+
MinSequenceContainer<int, bad_3way_random_access_iterator<int*>, bad_3way_random_access_iterator<const int*>>;
75+
using FMap = std::flat_map<int, int, std::less<int>, KeyCont, MinSequenceContainer<int>>;
76+
FMap m;
77+
// expected-error-re@*:* {{static assertion failed due to requirement 'is_same_v<std::weak_ordering, std::strong_ordering>'{{.*}}three-way comparison between random accesss container iterators must return std::strong_ordering}}
78+
// expected-error-re@*:* {{no viable conversion from returned value of type{{.*}}}}
79+
(void)(m.begin() <=> m.begin());
80+
}
81+
}

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

+26-29
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ void test() {
2828
using Key = typename KeyContainer::value_type;
2929
using Value = typename ValueContainer::value_type;
3030
using M = std::flat_multimap<Key, Value, std::less<Key>, KeyContainer, ValueContainer>;
31-
using KI = typename KeyContainer::iterator;
3231
using I = M::iterator;
3332
using CI = M::const_iterator;
3433
using RI = M::reverse_iterator;
@@ -115,34 +114,32 @@ void test() {
115114
assert(cri2 >= cri2);
116115
assert(!(cri1 >= cri2));
117116

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-
}
117+
static_assert(std::three_way_comparable<I>); // ...of course the wrapped iterators still support <=>.
118+
static_assert(std::three_way_comparable<CI>);
119+
static_assert(std::three_way_comparable<RI>);
120+
static_assert(std::three_way_comparable<CRI>);
121+
static_assert(std::same_as<decltype(I() <=> I()), std::strong_ordering>);
122+
static_assert(std::same_as<decltype(I() <=> CI()), std::strong_ordering>);
123+
static_assert(std::same_as<decltype(CI() <=> CI()), std::strong_ordering>);
124+
static_assert(std::same_as<decltype(RI() <=> RI()), std::strong_ordering>);
125+
static_assert(std::same_as<decltype(RI() <=> CRI()), std::strong_ordering>);
126+
static_assert(std::same_as<decltype(CRI() <=> CRI()), std::strong_ordering>);
127+
128+
assert(i1 <=> i1 == std::strong_ordering::equivalent);
129+
assert(i1 <=> i2 == std::strong_ordering::less);
130+
assert(i2 <=> i1 == std::strong_ordering::greater);
131+
132+
assert(ci1 <=> ci1 == std::strong_ordering::equivalent);
133+
assert(ci1 <=> ci2 == std::strong_ordering::less);
134+
assert(ci2 <=> ci1 == std::strong_ordering::greater);
135+
136+
assert(ri1 <=> ri1 == std::strong_ordering::equivalent);
137+
assert(ri1 <=> ri2 == std::strong_ordering::less);
138+
assert(ri2 <=> ri1 == std::strong_ordering::greater);
139+
140+
assert(cri1 <=> cri1 == std::strong_ordering::equivalent);
141+
assert(cri1 <=> cri2 == std::strong_ordering::less);
142+
assert(cri2 <=> cri1 == std::strong_ordering::greater);
146143
}
147144

148145
int main(int, char**) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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 <flat_map>
16+
#include <functional>
17+
#include <iterator>
18+
#include <type_traits>
19+
20+
#include "MinSequenceContainer.h"
21+
#include "test_iterators.h"
22+
23+
template <class It>
24+
class bad_3way_random_access_iterator {
25+
template <class U>
26+
friend class bad_3way_random_access_iterator;
27+
28+
public:
29+
using iterator_category = std::random_access_iterator_tag;
30+
using value_type = typename std::iterator_traits<It>::value_type;
31+
using difference_type = typename std::iterator_traits<It>::difference_type;
32+
using pointer = It;
33+
using reference = typename std::iterator_traits<It>::reference;
34+
35+
bad_3way_random_access_iterator();
36+
explicit bad_3way_random_access_iterator(It);
37+
38+
template <class U>
39+
bad_3way_random_access_iterator(const bad_3way_random_access_iterator<U>&);
40+
41+
template <class U, class = typename std::enable_if<std::is_default_constructible<U>::value>::type>
42+
bad_3way_random_access_iterator(bad_3way_random_access_iterator<U>&&);
43+
44+
reference operator*() const;
45+
reference operator[](difference_type) const;
46+
47+
bad_3way_random_access_iterator& operator++();
48+
bad_3way_random_access_iterator& operator--();
49+
bad_3way_random_access_iterator operator++(int);
50+
bad_3way_random_access_iterator operator--(int);
51+
52+
bad_3way_random_access_iterator& operator+=(difference_type);
53+
bad_3way_random_access_iterator& operator-=(difference_type);
54+
friend bad_3way_random_access_iterator operator+(bad_3way_random_access_iterator, difference_type);
55+
friend bad_3way_random_access_iterator operator+(difference_type, bad_3way_random_access_iterator);
56+
friend bad_3way_random_access_iterator operator-(bad_3way_random_access_iterator, difference_type);
57+
friend difference_type operator-(bad_3way_random_access_iterator, bad_3way_random_access_iterator);
58+
59+
friend bool operator==(const bad_3way_random_access_iterator&, const bad_3way_random_access_iterator&);
60+
friend std::weak_ordering operator<=>(const bad_3way_random_access_iterator&, const bad_3way_random_access_iterator&);
61+
};
62+
63+
void test() {
64+
{
65+
using KeyCont = MinSequenceContainer<int, random_access_iterator<int*>, random_access_iterator<const int*>>;
66+
using FMap = std::flat_multimap<int, int, std::less<int>, KeyCont, MinSequenceContainer<int>>;
67+
FMap m;
68+
// expected-error@*:* {{static assertion failed: random accesss iterator not supporting three-way comparison is invalid for container}}
69+
// expected-error@*:* {{invalid operands to binary expression}}
70+
(void)(m.begin() <=> m.begin());
71+
}
72+
{
73+
using KeyCont =
74+
MinSequenceContainer<int, bad_3way_random_access_iterator<int*>, bad_3way_random_access_iterator<const int*>>;
75+
using FMap = std::flat_multimap<int, int, std::less<int>, KeyCont, MinSequenceContainer<int>>;
76+
FMap m;
77+
// expected-error-re@*:* {{static assertion failed due to requirement 'is_same_v<std::weak_ordering, std::strong_ordering>'{{.*}}three-way comparison between random accesss container iterators must return std::strong_ordering}}
78+
// expected-error-re@*:* {{no viable conversion from returned value of type{{.*}}}}
79+
(void)(m.begin() <=> m.begin());
80+
}
81+
}

libcxx/test/support/MinSequenceContainer.h

+3-1
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)