Skip to content

Conversation

@huixie90
Copy link
Member

@huixie90 huixie90 commented Mar 30, 2025

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 P2865R6.

We should just use std::upper_bound instead.

@huixie90 huixie90 marked this pull request as ready for review April 4, 2025 14:37
@huixie90 huixie90 requested a review from a team as a code owner April 4, 2025 14:37
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Apr 4, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 4, 2025

@llvm/pr-subscribers-libcxx

Author: Hui (huixie90)

Changes

Patch is 37.71 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/133654.diff

31 Files Affected:

  • (modified) libcxx/include/__flat_map/flat_map.h (+22-27)
  • (modified) libcxx/include/__flat_map/flat_multimap.h (+12-11)
  • (modified) libcxx/include/__flat_set/flat_set.h (-1)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.map/flat.map.access/at_transparent.pass.cpp (+7)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.map/flat.map.access/index_transparent.pass.cpp (+7)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/erase_key_transparent.pass.cpp (+7)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_or_assign_transparent.pass.cpp (+12)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_transparent.pass.cpp (+10)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/try_emplace_transparent.pass.cpp (+11)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/contains_transparent.pass.cpp (+8)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/count_transparent.pass.cpp (+8)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/equal_range_transparent.pass.cpp (+9)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/find_transparent.pass.cpp (+8)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/lower_bound_transparent.pass.cpp (+8)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/upper_bound_transparent.pass.cpp (+8)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.modifiers/erase_key_transparent.pass.cpp (+7)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.modifiers/insert_transparent.pass.cpp (+9)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.operations/contains_transparent.pass.cpp (+8)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.operations/count_transparent.pass.cpp (+8)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.operations/equal_range_transparent.pass.cpp (+9)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.operations/find_transparent.pass.cpp (+8)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.operations/lower_bound_transparent.pass.cpp (+8)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.operations/upper_bound_transparent.pass.cpp (+8)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.set/flat.set.modifiers/erase_key_transparent.pass.cpp (+7)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.set/flat.set.modifiers/insert_transparent.pass.cpp (+8)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.set/flat.set.operations/contains_transparent.pass.cpp (+8)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.set/flat.set.operations/count_transparent.pass.cpp (+8)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.set/flat.set.operations/equal_range_transparent.pass.cpp (+9)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.set/flat.set.operations/find_transparent.pass.cpp (+10)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.set/flat.set.operations/lower_bound_transparent.pass.cpp (+10)
  • (modified) libcxx/test/std/containers/container.adaptors/flat.set/flat.set.operations/upper_bound_transparent.pass.cpp (+10)
diff --git a/libcxx/include/__flat_map/flat_map.h b/libcxx/include/__flat_map/flat_map.h
index f5abfd0985280..76a51bed50744 100644
--- a/libcxx/include/__flat_map/flat_map.h
+++ b/libcxx/include/__flat_map/flat_map.h
@@ -11,16 +11,15 @@
 #define _LIBCPP___FLAT_MAP_FLAT_MAP_H
 
 #include <__algorithm/lexicographical_compare_three_way.h>
+#include <__algorithm/lower_bound.h>
 #include <__algorithm/min.h>
 #include <__algorithm/ranges_adjacent_find.h>
 #include <__algorithm/ranges_equal.h>
 #include <__algorithm/ranges_inplace_merge.h>
-#include <__algorithm/ranges_lower_bound.h>
-#include <__algorithm/ranges_partition_point.h>
 #include <__algorithm/ranges_sort.h>
 #include <__algorithm/ranges_unique.h>
-#include <__algorithm/ranges_upper_bound.h>
 #include <__algorithm/remove_if.h>
+#include <__algorithm/upper_bound.h>
 #include <__assert>
 #include <__compare/synth_three_way.h>
 #include <__concepts/swappable.h>
@@ -863,6 +862,13 @@ class flat_map {
     __containers_.values.erase(__containers_.values.begin() + __dist, __containers_.values.end());
   }
 
+  template <class _Self, class _KeyIter>
+  _LIBCPP_HIDE_FROM_ABI static auto __corresponding_mapped_it(_Self&& __self, _KeyIter&& __key_iter) {
+    return __self.__containers_.values.begin() +
+           static_cast<ranges::range_difference_t<mapped_container_type>>(
+               ranges::distance(__self.__containers_.keys.begin(), __key_iter));
+  }
+
   template <bool _WasSorted, class _InputIterator, class _Sentinel>
   _LIBCPP_HIDE_FROM_ABI void __append_sort_merge_unique(_InputIterator __first, _Sentinel __last) {
     auto __on_failure        = std::__make_exception_guard([&]() noexcept { clear() /* noexcept */; });
@@ -903,7 +909,8 @@ class flat_map {
 
   template <class _Self, class _Kp>
   _LIBCPP_HIDE_FROM_ABI static auto __key_equal_range(_Self&& __self, const _Kp& __key) {
-    auto __it   = ranges::lower_bound(__self.__containers_.keys, __key, __self.__compare_);
+    auto __it =
+        std::lower_bound(__self.__containers_.keys.begin(), __self.__containers_.keys.end(), __key, __self.__compare_);
     auto __last = __self.__containers_.keys.end();
     if (__it == __last || __self.__compare_(__key, *__it)) {
       return std::make_pair(__it, __it);
@@ -914,42 +921,30 @@ class flat_map {
   template <class _Self, class _Kp>
   _LIBCPP_HIDE_FROM_ABI static auto __equal_range_impl(_Self&& __self, const _Kp& __key) {
     auto [__key_first, __key_last] = __key_equal_range(__self, __key);
-
-    const auto __make_mapped_iter = [&](const auto& __key_iter) {
-      return __self.__containers_.values.begin() +
-             static_cast<ranges::range_difference_t<mapped_container_type>>(
-                 ranges::distance(__self.__containers_.keys.begin(), __key_iter));
-    };
-
-    using __iterator_type = ranges::iterator_t<decltype(__self)>;
-    return std::make_pair(__iterator_type(__key_first, __make_mapped_iter(__key_first)),
-                          __iterator_type(__key_last, __make_mapped_iter(__key_last)));
+    using __iterator_type          = ranges::iterator_t<decltype(__self)>;
+    return std::make_pair(__iterator_type(__key_first, __corresponding_mapped_it(__self, __key_first)),
+                          __iterator_type(__key_last, __corresponding_mapped_it(__self, __key_last)));
   }
 
   template <class _Res, class _Self, class _Kp>
   _LIBCPP_HIDE_FROM_ABI static _Res __lower_bound(_Self&& __self, _Kp& __x) {
-    return __binary_search<_Res>(__self, ranges::lower_bound, __x);
+    auto __key_iter =
+        std::lower_bound(__self.__containers_.keys.begin(), __self.__containers_.keys.end(), __x, __self.__compare_);
+    auto __mapped_iter = __corresponding_mapped_it(__self, __key_iter);
+    return _Res(std::move(__key_iter), std::move(__mapped_iter));
   }
 
   template <class _Res, class _Self, class _Kp>
   _LIBCPP_HIDE_FROM_ABI static _Res __upper_bound(_Self&& __self, _Kp& __x) {
-    return __binary_search<_Res>(__self, ranges::upper_bound, __x);
-  }
-
-  template <class _Res, class _Self, class _Fn, class _Kp>
-  _LIBCPP_HIDE_FROM_ABI static _Res __binary_search(_Self&& __self, _Fn __search_fn, _Kp& __x) {
-    auto __key_iter = __search_fn(__self.__containers_.keys, __x, __self.__compare_);
-    auto __mapped_iter =
-        __self.__containers_.values.begin() +
-        static_cast<ranges::range_difference_t<mapped_container_type>>(
-            ranges::distance(__self.__containers_.keys.begin(), __key_iter));
-
+    auto __key_iter =
+        std::upper_bound(__self.__containers_.keys.begin(), __self.__containers_.keys.end(), __x, __self.__compare_);
+    auto __mapped_iter = __corresponding_mapped_it(__self, __key_iter);
     return _Res(std::move(__key_iter), std::move(__mapped_iter));
   }
 
   template <class _KeyArg, class... _MArgs>
   _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> __try_emplace(_KeyArg&& __key, _MArgs&&... __mapped_args) {
-    auto __key_it    = ranges::lower_bound(__containers_.keys, __key, __compare_);
+    auto __key_it    = std::lower_bound(__containers_.keys.begin(), __containers_.keys.end(), __key, __compare_);
     auto __mapped_it = __containers_.values.begin() + ranges::distance(__containers_.keys.begin(), __key_it);
 
     if (__key_it == __containers_.keys.end() || __compare_(__key, *__key_it)) {
diff --git a/libcxx/include/__flat_map/flat_multimap.h b/libcxx/include/__flat_map/flat_multimap.h
index ea77fb5d79bd2..7b5c64e0b5262 100644
--- a/libcxx/include/__flat_map/flat_multimap.h
+++ b/libcxx/include/__flat_map/flat_multimap.h
@@ -10,18 +10,16 @@
 #ifndef _LIBCPP___FLAT_MAP_FLAT_MULTIMAP_H
 #define _LIBCPP___FLAT_MAP_FLAT_MULTIMAP_H
 
+#include <__algorithm/equal_range.h>
 #include <__algorithm/lexicographical_compare_three_way.h>
+#include <__algorithm/lower_bound.h>
 #include <__algorithm/min.h>
 #include <__algorithm/ranges_equal.h>
-#include <__algorithm/ranges_equal_range.h>
 #include <__algorithm/ranges_inplace_merge.h>
 #include <__algorithm/ranges_is_sorted.h>
-#include <__algorithm/ranges_lower_bound.h>
-#include <__algorithm/ranges_partition_point.h>
 #include <__algorithm/ranges_sort.h>
-#include <__algorithm/ranges_unique.h>
-#include <__algorithm/ranges_upper_bound.h>
 #include <__algorithm/remove_if.h>
+#include <__algorithm/upper_bound.h>
 #include <__assert>
 #include <__compare/synth_three_way.h>
 #include <__concepts/convertible_to.h>
@@ -443,7 +441,7 @@ class flat_multimap {
              is_move_constructible_v<mapped_type>
   _LIBCPP_HIDE_FROM_ABI iterator emplace(_Args&&... __args) {
     std::pair<key_type, mapped_type> __pair(std::forward<_Args>(__args)...);
-    auto __key_it    = ranges::upper_bound(__containers_.keys, __pair.first, __compare_);
+    auto __key_it    = std::upper_bound(__containers_.keys.begin(), __containers_.keys.end(), __pair.first, __compare_);
     auto __mapped_it = __corresponding_mapped_it(*this, __key_it);
 
     return __flat_map_utils::__emplace_exact_pos(
@@ -473,7 +471,7 @@ class flat_multimap {
       //                   |
       //                  hint
       // We want to insert "2" after the last existing "2"
-      __key_iter    = ranges::upper_bound(__containers_.keys.begin(), __key_iter, __pair.first, __compare_);
+      __key_iter    = std::upper_bound(__containers_.keys.begin(), __key_iter, __pair.first, __compare_);
       __mapped_iter = __corresponding_mapped_it(*this, __key_iter);
     } else {
       _LIBCPP_ASSERT_INTERNAL(!__prev_larger && __next_smaller, "this means that the multimap is not sorted");
@@ -485,7 +483,7 @@ class flat_multimap {
       //  |
       // hint
       // We want to insert "2" before the first existing "2"
-      __key_iter    = ranges::lower_bound(__key_iter, __containers_.keys.end(), __pair.first, __compare_);
+      __key_iter    = std::lower_bound(__key_iter, __containers_.keys.end(), __pair.first, __compare_);
       __mapped_iter = __corresponding_mapped_it(*this, __key_iter);
     }
     return __flat_map_utils::__emplace_exact_pos(
@@ -804,7 +802,8 @@ class flat_multimap {
 
   template <class _Self, class _Kp>
   _LIBCPP_HIDE_FROM_ABI static auto __equal_range_impl(_Self&& __self, const _Kp& __key) {
-    auto [__key_first, __key_last] = ranges::equal_range(__self.__containers_.keys, __key, __self.__compare_);
+    auto [__key_first, __key_last] =
+        std::equal_range(__self.__containers_.keys.begin(), __self.__containers_.keys.end(), __key, __self.__compare_);
 
     using __iterator_type = ranges::iterator_t<decltype(__self)>;
     return std::make_pair(__iterator_type(__key_first, __corresponding_mapped_it(__self, __key_first)),
@@ -813,14 +812,16 @@ class flat_multimap {
 
   template <class _Res, class _Self, class _Kp>
   _LIBCPP_HIDE_FROM_ABI static _Res __lower_bound(_Self&& __self, _Kp& __x) {
-    auto __key_iter    = ranges::lower_bound(__self.__containers_.keys, __x, __self.__compare_);
+    auto __key_iter =
+        std::lower_bound(__self.__containers_.keys.begin(), __self.__containers_.keys.end(), __x, __self.__compare_);
     auto __mapped_iter = __corresponding_mapped_it(__self, __key_iter);
     return _Res(std::move(__key_iter), std::move(__mapped_iter));
   }
 
   template <class _Res, class _Self, class _Kp>
   _LIBCPP_HIDE_FROM_ABI static _Res __upper_bound(_Self&& __self, _Kp& __x) {
-    auto __key_iter    = ranges::upper_bound(__self.__containers_.keys, __x, __self.__compare_);
+    auto __key_iter =
+        std::upper_bound(__self.__containers_.keys.begin(), __self.__containers_.keys.end(), __x, __self.__compare_);
     auto __mapped_iter = __corresponding_mapped_it(__self, __key_iter);
     return _Res(std::move(__key_iter), std::move(__mapped_iter));
   }
diff --git a/libcxx/include/__flat_set/flat_set.h b/libcxx/include/__flat_set/flat_set.h
index dca06c7236e73..b554d7555997e 100644
--- a/libcxx/include/__flat_set/flat_set.h
+++ b/libcxx/include/__flat_set/flat_set.h
@@ -16,7 +16,6 @@
 #include <__algorithm/ranges_adjacent_find.h>
 #include <__algorithm/ranges_equal.h>
 #include <__algorithm/ranges_inplace_merge.h>
-#include <__algorithm/ranges_partition_point.h>
 #include <__algorithm/ranges_sort.h>
 #include <__algorithm/ranges_unique.h>
 #include <__algorithm/remove_if.h>
diff --git a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.access/at_transparent.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.access/at_transparent.pass.cpp
index 456f12e0c0d29..dd9f6da2dda71 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.access/at_transparent.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.access/at_transparent.pass.cpp
@@ -106,6 +106,13 @@ int main(int, char**) {
     TEST_IGNORE_NODISCARD m.at(Transparent<int>{3});
     assert(transparent_used);
   }
+  {
+    // std::string and C string literal
+    using M = std::flat_map<std::string, int, std::less<>>;
+    M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
+    int& x = m.at("alpha");
+    assert(x == 1);
+  }
 
   return 0;
 }
diff --git a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.access/index_transparent.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.access/index_transparent.pass.cpp
index 24c08464f3158..3a18990123553 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.access/index_transparent.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.access/index_transparent.pass.cpp
@@ -94,6 +94,13 @@ int main(int, char**) {
     m[ConvertibleTransparent<int>{3}];
     assert(transparent_used);
   }
+  {
+    // std::string and C string literal
+    using M = std::flat_map<std::string, int, std::less<>>;
+    M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
+    int& x = m["alpha"];
+    assert(x == 1);
+  }
   {
     auto index_func = [](auto& m, auto key_arg, auto value_arg) {
       using FlatMap                             = std::decay_t<decltype(m)>;
diff --git a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/erase_key_transparent.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/erase_key_transparent.pass.cpp
index 3ba30757bf2c7..20e23dc085ffd 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/erase_key_transparent.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/erase_key_transparent.pass.cpp
@@ -139,6 +139,13 @@ int main(int, char**) {
     };
     test_erase_exception_guarantee(erase_transparent);
   }
+  {
+    // std::string and C string literal
+    using M = std::flat_map<std::string, int, std::less<>>;
+    M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
+    auto n = m.erase("beta");
+    assert(n == 1);
+  }
 
   return 0;
 }
diff --git a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_or_assign_transparent.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_or_assign_transparent.pass.cpp
index 636c4edfe551d..3a1e9eb7482b0 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_or_assign_transparent.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_or_assign_transparent.pass.cpp
@@ -254,6 +254,18 @@ int main(int, char**) {
     };
     test_emplace_exception_guarantee(insert_or_assign_iter);
   }
+  {
+    // std::string and C string literal
+    using M = std::flat_map<std::string, int, std::less<>>;
+    M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
+    auto [it, inserted] = m.insert_or_assign("alpha", 2);
+    assert(!inserted);
+    assert(it == m.begin());
+    assert(it->second == 2);
+    auto it2 = m.insert_or_assign(m.begin(), "beta2", 2);
+    assert(it2 == m.begin() + 2);
+    assert(it2->second == 2);
+  }
 
   return 0;
 }
diff --git a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_transparent.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_transparent.pass.cpp
index 75cabb70630f3..b6a2abe5e5e01 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_transparent.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_transparent.pass.cpp
@@ -163,5 +163,15 @@ int main(int, char**) {
     };
     test_emplace_exception_guarantee(insert_func_iter);
   }
+  {
+    // std::string and C string literal
+    using M = std::flat_map<std::string, int, std::less<>>;
+    M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
+    auto [it, inserted] = m.insert({"alpha", 1});
+    assert(!inserted);
+    assert(it == m.begin());
+    auto it2 = m.insert(m.begin(), {"beta2", 2});
+    assert(it2 == m.begin() + 2);
+  }
   return 0;
 }
diff --git a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/try_emplace_transparent.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/try_emplace_transparent.pass.cpp
index 21fda43780967..b8dd590ac808e 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/try_emplace_transparent.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/try_emplace_transparent.pass.cpp
@@ -160,6 +160,17 @@ int main(int, char**) {
     assert(p->second == 3);
     assert(transparent_used);
   }
+  {
+    // std::string and C string literal
+    using M = std::flat_map<std::string, int, std::less<>>;
+    M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
+    auto [it1, inserted] = m.try_emplace("charlie", 4);
+    assert(it1 == m.begin() + 2);
+    assert(inserted);
+
+    auto it2 = m.try_emplace(m.begin(), "beta2", 2);
+    assert(it2 == m.begin() + 2);
+  }
   {
     auto try_emplace = [](auto& m, auto key_arg, auto value_arg) {
       using M   = std::decay_t<decltype(m)>;
diff --git a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/contains_transparent.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/contains_transparent.pass.cpp
index 0493538ab6dad..46863681ae6ef 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/contains_transparent.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/contains_transparent.pass.cpp
@@ -14,6 +14,7 @@
 
 #include <cassert>
 #include <flat_map>
+#include <functional>
 #include <string>
 #include <utility>
 #include <deque>
@@ -67,5 +68,12 @@ int main(int, char**) {
     assert(b);
     assert(transparent_used);
   }
+  {
+    // std::string and C string literal
+    using M = std::flat_map<std::string, int, std::less<>>;
+    M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
+    assert(m.contains("beta") == true);
+    assert(m.contains("charlie") == false);
+  }
   return 0;
 }
diff --git a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/count_transparent.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/count_transparent.pass.cpp
index cd195ff1fa8b4..946ded0b1cfab 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/count_transparent.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/count_transparent.pass.cpp
@@ -15,6 +15,7 @@
 #include <cassert>
 #include <deque>
 #include <flat_map>
+#include <functional>
 #include <string>
 #include <utility>
 
@@ -67,6 +68,13 @@ int main(int, char**) {
     assert(n == 1);
     assert(transparent_used);
   }
+  {
+    // std::string and C string literal
+    using M = std::flat_map<std::string, int, std::less<>>;
+    M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
+    assert(m.count("alpha") == 1);
+    assert(m.count("charlie") == 0);
+  }
 
   return 0;
 }
diff --git a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/equal_range_transparent.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/equal_range_transparent.pass.cpp
index 0198f433bdc4f..9d623823d430e 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/equal_range_transparent.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/equal_range_transparent.pass.cpp
@@ -16,6 +16,7 @@
 #include <cassert>
 #include <deque>
 #include <flat_map>
+#include <functional>
 #include <string>
 #include <utility>
 
@@ -95,6 +96,14 @@ int main(int, char**) {
     assert(p.first != p.second);
     assert(transparent_used);
   }
+  {
+    // std::string and C string literal
+    using M = std::flat_map<std::string, int, std::less<>>;
+    M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
+    auto [first, last] = m.equal_range("beta");
+    assert(first == m.begin() + 1);
+    assert(last == m.begin() + 2);
+  }
 
   return 0;
 }
diff --git a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/find_transparent.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/find_transparent.pass.cpp
index 291577a89fc8f..249732b757662 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/find_transparent.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/find_transparent.pass.cpp
@@ -16,6 +16,7 @@
 #include <cassert>
 #include <deque>
 #include <flat_map>
+#include <functional>
 #include <string>
 #include <utility>
 
@@ -83,6 +84,13 @@ int main(int, char**) {
     assert(it != m.end());
     assert(transparent_used);
   }
+  {
+    // std::string and C string literal
+    using M = std::flat_map<std::string, int, std::less<>>;
+    M m{{"alpha", 1}, {"beta", 2}, {"epsilon", 1}, {"eta", 3}, {"gamma", 3}};
+    auto it = m.find("beta");
+    assert(it == m.begin() + 1);
+  }
 
   return 0;
 }
diff --git a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/lower_bound_transparent.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.operations/lower_bound_transparent.pass.cpp
in...
[truncated]

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with minor comments for leaving traces/context behind. Thanks a lot!

I hope this gets accepted as a LWG issue and fixed somehow -- this really should work out of the box.

@ldionne ldionne merged commit 155fd97 into llvm:main Jun 6, 2025
133 of 135 checks passed
tomtor pushed a commit to tomtor/llvm-project that referenced this pull request Jun 14, 2025
)

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants