Skip to content

Commit 155fd97

Browse files
authored
[libc++] flat_meow transparent comparator string literals (#133654)
See discussion in https://cplusplus.github.io/LWG/issue4239 std::flat_map<std::string, int, std::less<>> m; m.try_emplace("abc", 5); // hard error The reason is that we specify in 23.6.8.7 [flat.map.modifiers]/p21 the effect to be as if `ranges::upper_bound` is called. `ranges::upper_bound` requires indirect_strict_weak_order, which requires the comparator to be invocable for all combinations. In this case, it requires const char (&)[4] < const char (&)[4] to be well-formed, which is no longer the case in C++26 after https://wg21.link/P2865R6. This patch uses `std::upper_bound` instead.
1 parent b0f53d9 commit 155fd97

31 files changed

+272
-39
lines changed

libcxx/include/__flat_map/flat_map.h

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,15 @@
1111
#define _LIBCPP___FLAT_MAP_FLAT_MAP_H
1212

1313
#include <__algorithm/lexicographical_compare_three_way.h>
14+
#include <__algorithm/lower_bound.h>
1415
#include <__algorithm/min.h>
1516
#include <__algorithm/ranges_adjacent_find.h>
1617
#include <__algorithm/ranges_equal.h>
1718
#include <__algorithm/ranges_inplace_merge.h>
18-
#include <__algorithm/ranges_lower_bound.h>
19-
#include <__algorithm/ranges_partition_point.h>
2019
#include <__algorithm/ranges_sort.h>
2120
#include <__algorithm/ranges_unique.h>
22-
#include <__algorithm/ranges_upper_bound.h>
2321
#include <__algorithm/remove_if.h>
22+
#include <__algorithm/upper_bound.h>
2423
#include <__assert>
2524
#include <__compare/synth_three_way.h>
2625
#include <__concepts/swappable.h>
@@ -863,6 +862,13 @@ class flat_map {
863862
__containers_.values.erase(__containers_.values.begin() + __dist, __containers_.values.end());
864863
}
865864

865+
template <class _Self, class _KeyIter>
866+
_LIBCPP_HIDE_FROM_ABI static auto __corresponding_mapped_it(_Self&& __self, _KeyIter&& __key_iter) {
867+
return __self.__containers_.values.begin() +
868+
static_cast<ranges::range_difference_t<mapped_container_type>>(
869+
ranges::distance(__self.__containers_.keys.begin(), __key_iter));
870+
}
871+
866872
template <bool _WasSorted, class _InputIterator, class _Sentinel>
867873
_LIBCPP_HIDE_FROM_ABI void __append_sort_merge_unique(_InputIterator __first, _Sentinel __last) {
868874
auto __on_failure = std::__make_exception_guard([&]() noexcept { clear() /* noexcept */; });
@@ -903,7 +909,8 @@ class flat_map {
903909

904910
template <class _Self, class _Kp>
905911
_LIBCPP_HIDE_FROM_ABI static auto __key_equal_range(_Self&& __self, const _Kp& __key) {
906-
auto __it = ranges::lower_bound(__self.__containers_.keys, __key, __self.__compare_);
912+
auto __it =
913+
std::lower_bound(__self.__containers_.keys.begin(), __self.__containers_.keys.end(), __key, __self.__compare_);
907914
auto __last = __self.__containers_.keys.end();
908915
if (__it == __last || __self.__compare_(__key, *__it)) {
909916
return std::make_pair(__it, __it);
@@ -914,42 +921,30 @@ class flat_map {
914921
template <class _Self, class _Kp>
915922
_LIBCPP_HIDE_FROM_ABI static auto __equal_range_impl(_Self&& __self, const _Kp& __key) {
916923
auto [__key_first, __key_last] = __key_equal_range(__self, __key);
917-
918-
const auto __make_mapped_iter = [&](const auto& __key_iter) {
919-
return __self.__containers_.values.begin() +
920-
static_cast<ranges::range_difference_t<mapped_container_type>>(
921-
ranges::distance(__self.__containers_.keys.begin(), __key_iter));
922-
};
923-
924-
using __iterator_type = ranges::iterator_t<decltype(__self)>;
925-
return std::make_pair(__iterator_type(__key_first, __make_mapped_iter(__key_first)),
926-
__iterator_type(__key_last, __make_mapped_iter(__key_last)));
924+
using __iterator_type = ranges::iterator_t<decltype(__self)>;
925+
return std::make_pair(__iterator_type(__key_first, __corresponding_mapped_it(__self, __key_first)),
926+
__iterator_type(__key_last, __corresponding_mapped_it(__self, __key_last)));
927927
}
928928

929929
template <class _Res, class _Self, class _Kp>
930930
_LIBCPP_HIDE_FROM_ABI static _Res __lower_bound(_Self&& __self, _Kp& __x) {
931-
return __binary_search<_Res>(__self, ranges::lower_bound, __x);
931+
auto __key_iter =
932+
std::lower_bound(__self.__containers_.keys.begin(), __self.__containers_.keys.end(), __x, __self.__compare_);
933+
auto __mapped_iter = __corresponding_mapped_it(__self, __key_iter);
934+
return _Res(std::move(__key_iter), std::move(__mapped_iter));
932935
}
933936

934937
template <class _Res, class _Self, class _Kp>
935938
_LIBCPP_HIDE_FROM_ABI static _Res __upper_bound(_Self&& __self, _Kp& __x) {
936-
return __binary_search<_Res>(__self, ranges::upper_bound, __x);
937-
}
938-
939-
template <class _Res, class _Self, class _Fn, class _Kp>
940-
_LIBCPP_HIDE_FROM_ABI static _Res __binary_search(_Self&& __self, _Fn __search_fn, _Kp& __x) {
941-
auto __key_iter = __search_fn(__self.__containers_.keys, __x, __self.__compare_);
942-
auto __mapped_iter =
943-
__self.__containers_.values.begin() +
944-
static_cast<ranges::range_difference_t<mapped_container_type>>(
945-
ranges::distance(__self.__containers_.keys.begin(), __key_iter));
946-
939+
auto __key_iter =
940+
std::upper_bound(__self.__containers_.keys.begin(), __self.__containers_.keys.end(), __x, __self.__compare_);
941+
auto __mapped_iter = __corresponding_mapped_it(__self, __key_iter);
947942
return _Res(std::move(__key_iter), std::move(__mapped_iter));
948943
}
949944

950945
template <class _KeyArg, class... _MArgs>
951946
_LIBCPP_HIDE_FROM_ABI pair<iterator, bool> __try_emplace(_KeyArg&& __key, _MArgs&&... __mapped_args) {
952-
auto __key_it = ranges::lower_bound(__containers_.keys, __key, __compare_);
947+
auto __key_it = std::lower_bound(__containers_.keys.begin(), __containers_.keys.end(), __key, __compare_);
953948
auto __mapped_it = __containers_.values.begin() + ranges::distance(__containers_.keys.begin(), __key_it);
954949

955950
if (__key_it == __containers_.keys.end() || __compare_(__key, *__key_it)) {

libcxx/include/__flat_map/flat_multimap.h

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,16 @@
1010
#ifndef _LIBCPP___FLAT_MAP_FLAT_MULTIMAP_H
1111
#define _LIBCPP___FLAT_MAP_FLAT_MULTIMAP_H
1212

13+
#include <__algorithm/equal_range.h>
1314
#include <__algorithm/lexicographical_compare_three_way.h>
15+
#include <__algorithm/lower_bound.h>
1416
#include <__algorithm/min.h>
1517
#include <__algorithm/ranges_equal.h>
16-
#include <__algorithm/ranges_equal_range.h>
1718
#include <__algorithm/ranges_inplace_merge.h>
1819
#include <__algorithm/ranges_is_sorted.h>
19-
#include <__algorithm/ranges_lower_bound.h>
20-
#include <__algorithm/ranges_partition_point.h>
2120
#include <__algorithm/ranges_sort.h>
22-
#include <__algorithm/ranges_unique.h>
23-
#include <__algorithm/ranges_upper_bound.h>
2421
#include <__algorithm/remove_if.h>
22+
#include <__algorithm/upper_bound.h>
2523
#include <__assert>
2624
#include <__compare/synth_three_way.h>
2725
#include <__concepts/convertible_to.h>
@@ -443,7 +441,7 @@ class flat_multimap {
443441
is_move_constructible_v<mapped_type>
444442
_LIBCPP_HIDE_FROM_ABI iterator emplace(_Args&&... __args) {
445443
std::pair<key_type, mapped_type> __pair(std::forward<_Args>(__args)...);
446-
auto __key_it = ranges::upper_bound(__containers_.keys, __pair.first, __compare_);
444+
auto __key_it = std::upper_bound(__containers_.keys.begin(), __containers_.keys.end(), __pair.first, __compare_);
447445
auto __mapped_it = __corresponding_mapped_it(*this, __key_it);
448446

449447
return __flat_map_utils::__emplace_exact_pos(
@@ -473,7 +471,7 @@ class flat_multimap {
473471
// |
474472
// hint
475473
// We want to insert "2" after the last existing "2"
476-
__key_iter = ranges::upper_bound(__containers_.keys.begin(), __key_iter, __pair.first, __compare_);
474+
__key_iter = std::upper_bound(__containers_.keys.begin(), __key_iter, __pair.first, __compare_);
477475
__mapped_iter = __corresponding_mapped_it(*this, __key_iter);
478476
} else {
479477
_LIBCPP_ASSERT_INTERNAL(!__prev_larger && __next_smaller, "this means that the multimap is not sorted");
@@ -485,7 +483,7 @@ class flat_multimap {
485483
// |
486484
// hint
487485
// We want to insert "2" before the first existing "2"
488-
__key_iter = ranges::lower_bound(__key_iter, __containers_.keys.end(), __pair.first, __compare_);
486+
__key_iter = std::lower_bound(__key_iter, __containers_.keys.end(), __pair.first, __compare_);
489487
__mapped_iter = __corresponding_mapped_it(*this, __key_iter);
490488
}
491489
return __flat_map_utils::__emplace_exact_pos(
@@ -804,7 +802,8 @@ class flat_multimap {
804802

805803
template <class _Self, class _Kp>
806804
_LIBCPP_HIDE_FROM_ABI static auto __equal_range_impl(_Self&& __self, const _Kp& __key) {
807-
auto [__key_first, __key_last] = ranges::equal_range(__self.__containers_.keys, __key, __self.__compare_);
805+
auto [__key_first, __key_last] =
806+
std::equal_range(__self.__containers_.keys.begin(), __self.__containers_.keys.end(), __key, __self.__compare_);
808807

809808
using __iterator_type = ranges::iterator_t<decltype(__self)>;
810809
return std::make_pair(__iterator_type(__key_first, __corresponding_mapped_it(__self, __key_first)),
@@ -813,14 +812,16 @@ class flat_multimap {
813812

814813
template <class _Res, class _Self, class _Kp>
815814
_LIBCPP_HIDE_FROM_ABI static _Res __lower_bound(_Self&& __self, _Kp& __x) {
816-
auto __key_iter = ranges::lower_bound(__self.__containers_.keys, __x, __self.__compare_);
815+
auto __key_iter =
816+
std::lower_bound(__self.__containers_.keys.begin(), __self.__containers_.keys.end(), __x, __self.__compare_);
817817
auto __mapped_iter = __corresponding_mapped_it(__self, __key_iter);
818818
return _Res(std::move(__key_iter), std::move(__mapped_iter));
819819
}
820820

821821
template <class _Res, class _Self, class _Kp>
822822
_LIBCPP_HIDE_FROM_ABI static _Res __upper_bound(_Self&& __self, _Kp& __x) {
823-
auto __key_iter = ranges::upper_bound(__self.__containers_.keys, __x, __self.__compare_);
823+
auto __key_iter =
824+
std::upper_bound(__self.__containers_.keys.begin(), __self.__containers_.keys.end(), __x, __self.__compare_);
824825
auto __mapped_iter = __corresponding_mapped_it(__self, __key_iter);
825826
return _Res(std::move(__key_iter), std::move(__mapped_iter));
826827
}

libcxx/include/__flat_set/flat_set.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
#include <__algorithm/ranges_adjacent_find.h>
1717
#include <__algorithm/ranges_equal.h>
1818
#include <__algorithm/ranges_inplace_merge.h>
19-
#include <__algorithm/ranges_partition_point.h>
2019
#include <__algorithm/ranges_sort.h>
2120
#include <__algorithm/ranges_unique.h>
2221
#include <__algorithm/remove_if.h>

libcxx/test/std/containers/container.adaptors/flat.map/flat.map.access/at_transparent.pass.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ int main(int, char**) {
106106
TEST_IGNORE_NODISCARD m.at(Transparent<int>{3});
107107
assert(transparent_used);
108108
}
109+
{
110+
// LWG4239 std::string and C string literal
111+
using M = std::flat_map<std::string, int, std::less<>>;
112+
M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
113+
int& x = m.at("alpha");
114+
assert(x == 1);
115+
}
109116

110117
return 0;
111118
}

libcxx/test/std/containers/container.adaptors/flat.map/flat.map.access/index_transparent.pass.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ int main(int, char**) {
9494
m[ConvertibleTransparent<int>{3}];
9595
assert(transparent_used);
9696
}
97+
{
98+
// LWG4239 std::string and C string literal
99+
using M = std::flat_map<std::string, int, std::less<>>;
100+
M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
101+
int& x = m["alpha"];
102+
assert(x == 1);
103+
}
97104
{
98105
auto index_func = [](auto& m, auto key_arg, auto value_arg) {
99106
using FlatMap = std::decay_t<decltype(m)>;

libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/erase_key_transparent.pass.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,13 @@ int main(int, char**) {
139139
};
140140
test_erase_exception_guarantee(erase_transparent);
141141
}
142+
{
143+
// LWG4239 std::string and C string literal
144+
using M = std::flat_map<std::string, int, std::less<>>;
145+
M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
146+
auto n = m.erase("beta");
147+
assert(n == 1);
148+
}
142149

143150
return 0;
144151
}

libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_or_assign_transparent.pass.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,18 @@ int main(int, char**) {
254254
};
255255
test_emplace_exception_guarantee(insert_or_assign_iter);
256256
}
257+
{
258+
// LWG4239 std::string and C string literal
259+
using M = std::flat_map<std::string, int, std::less<>>;
260+
M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
261+
auto [it, inserted] = m.insert_or_assign("alpha", 2);
262+
assert(!inserted);
263+
assert(it == m.begin());
264+
assert(it->second == 2);
265+
auto it2 = m.insert_or_assign(m.begin(), "beta2", 2);
266+
assert(it2 == m.begin() + 2);
267+
assert(it2->second == 2);
268+
}
257269

258270
return 0;
259271
}

libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_transparent.pass.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,5 +163,15 @@ int main(int, char**) {
163163
};
164164
test_emplace_exception_guarantee(insert_func_iter);
165165
}
166+
{
167+
// LWG4239 std::string and C string literal
168+
using M = std::flat_map<std::string, int, std::less<>>;
169+
M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
170+
auto [it, inserted] = m.insert({"alpha", 1});
171+
assert(!inserted);
172+
assert(it == m.begin());
173+
auto it2 = m.insert(m.begin(), {"beta2", 2});
174+
assert(it2 == m.begin() + 2);
175+
}
166176
return 0;
167177
}

libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/try_emplace_transparent.pass.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,17 @@ int main(int, char**) {
160160
assert(p->second == 3);
161161
assert(transparent_used);
162162
}
163+
{
164+
// LWG4239 std::string and C string literal
165+
using M = std::flat_map<std::string, int, std::less<>>;
166+
M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
167+
auto [it1, inserted] = m.try_emplace("charlie", 4);
168+
assert(it1 == m.begin() + 2);
169+
assert(inserted);
170+
171+
auto it2 = m.try_emplace(m.begin(), "beta2", 2);
172+
assert(it2 == m.begin() + 2);
173+
}
163174
{
164175
auto try_emplace = [](auto& m, auto key_arg, auto value_arg) {
165176
using M = std::decay_t<decltype(m)>;

libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/contains_transparent.pass.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include <cassert>
1616
#include <flat_map>
17+
#include <functional>
1718
#include <string>
1819
#include <utility>
1920
#include <deque>
@@ -67,5 +68,12 @@ int main(int, char**) {
6768
assert(b);
6869
assert(transparent_used);
6970
}
71+
{
72+
// LWG4239 std::string and C string literal
73+
using M = std::flat_map<std::string, int, std::less<>>;
74+
M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
75+
assert(m.contains("beta") == true);
76+
assert(m.contains("charlie") == false);
77+
}
7078
return 0;
7179
}

libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/count_transparent.pass.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <cassert>
1616
#include <deque>
1717
#include <flat_map>
18+
#include <functional>
1819
#include <string>
1920
#include <utility>
2021

@@ -67,6 +68,13 @@ int main(int, char**) {
6768
assert(n == 1);
6869
assert(transparent_used);
6970
}
71+
{
72+
// LWG4239 std::string and C string literal
73+
using M = std::flat_map<std::string, int, std::less<>>;
74+
M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
75+
assert(m.count("alpha") == 1);
76+
assert(m.count("charlie") == 0);
77+
}
7078

7179
return 0;
7280
}

libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/equal_range_transparent.pass.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <cassert>
1717
#include <deque>
1818
#include <flat_map>
19+
#include <functional>
1920
#include <string>
2021
#include <utility>
2122

@@ -95,6 +96,14 @@ int main(int, char**) {
9596
assert(p.first != p.second);
9697
assert(transparent_used);
9798
}
99+
{
100+
// LWG4239 std::string and C string literal
101+
using M = std::flat_map<std::string, int, std::less<>>;
102+
M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
103+
auto [first, last] = m.equal_range("beta");
104+
assert(first == m.begin() + 1);
105+
assert(last == m.begin() + 2);
106+
}
98107

99108
return 0;
100109
}

libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/find_transparent.pass.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <cassert>
1717
#include <deque>
1818
#include <flat_map>
19+
#include <functional>
1920
#include <string>
2021
#include <utility>
2122

@@ -83,6 +84,13 @@ int main(int, char**) {
8384
assert(it != m.end());
8485
assert(transparent_used);
8586
}
87+
{
88+
// LWG4239 std::string and C string literal
89+
using M = std::flat_map<std::string, int, std::less<>>;
90+
M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
91+
auto it = m.find("beta");
92+
assert(it == m.begin() + 1);
93+
}
8694

8795
return 0;
8896
}

libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/lower_bound_transparent.pass.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <cassert>
1717
#include <deque>
1818
#include <flat_map>
19+
#include <functional>
1920
#include <string>
2021
#include <utility>
2122

@@ -90,6 +91,13 @@ int main(int, char**) {
9091
assert(it != m.end());
9192
assert(transparent_used);
9293
}
94+
{
95+
// LWG4239 std::string and C string literal
96+
using M = std::flat_map<std::string, int, std::less<>>;
97+
M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
98+
auto it = m.lower_bound("charlie");
99+
assert(it == m.begin() + 2);
100+
}
93101

94102
return 0;
95103
}

0 commit comments

Comments
 (0)