[libcxx-commits] [libcxx] [DRAFT] [libc++] Add `__is_transparently_comparable_v` specializations for `basic_string_view` (PR #195871)
Michael Levine via libcxx-commits
libcxx-commits at lists.llvm.org
Wed May 6 11:53:40 PDT 2026
https://github.com/levinem updated https://github.com/llvm/llvm-project/pull/195871
>From a25cc50d919a949e3403f91b499f2db76a0f463b Mon Sep 17 00:00:00 2001
From: mlevine55 <mlevine55 at bloomberg.net>
Date: Tue, 5 May 2026 11:15:47 -0400
Subject: [PATCH 01/10] Added the optimizations
Signed-off-by: mlevine55 <mlevine55 at bloomberg.net>
---
libcxx/include/string | 23 +++++++++++++++
libcxx/include/string_view | 27 ++++++++++++++++++
..._transparently_comparable.compile.pass.cpp | 28 +++++++++++++++++++
3 files changed, 78 insertions(+)
diff --git a/libcxx/include/string b/libcxx/include/string
index a00b824e5150a..82deee9085027 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -2599,6 +2599,29 @@ template <class _Comparator, class _CharT, class _Traits, class _Alloc, size_t _
inline const bool __is_transparently_comparable_v<_Comparator, _CharT[_Np], basic_string<_CharT, _Traits, _Alloc> > =
__is_transparently_comparable_v<_Comparator, basic_string<_CharT, _Traits, _Alloc>, const _CharT*>;
+template <class _Comparator, class _CharT, class _Traits, class _Alloc>
+inline const bool
+ __is_transparently_comparable_v<_Comparator,
+ basic_string<_CharT, _Traits, _Alloc>,
+ basic_string_view<_CharT, _Traits> > =
+ __desugars_to_v<__less_tag,
+ _Comparator,
+ basic_string<_CharT, _Traits, _Alloc>,
+ basic_string<_CharT, _Traits, _Alloc> > ||
+ __desugars_to_v<__greater_tag,
+ _Comparator,
+ basic_string<_CharT, _Traits, _Alloc>,
+ basic_string<_CharT, _Traits, _Alloc> >;
+
+template <class _Comparator, class _CharT, class _Traits, class _Alloc>
+inline const bool
+ __is_transparently_comparable_v<_Comparator,
+ basic_string_view<_CharT, _Traits>,
+ basic_string<_CharT, _Traits, _Alloc> > =
+ __is_transparently_comparable_v<_Comparator,
+ basic_string<_CharT, _Traits, _Alloc>,
+ basic_string_view<_CharT, _Traits> >;
+
# if _LIBCPP_STD_VER >= 17
template <class _InputIterator,
class _CharT = __iterator_value_type<_InputIterator>,
diff --git a/libcxx/include/string_view b/libcxx/include/string_view
index 5dd04a9ba8479..0ea8b5f3965f5 100644
--- a/libcxx/include/string_view
+++ b/libcxx/include/string_view
@@ -217,6 +217,7 @@ namespace std {
# include <__cstddef/ptrdiff_t.h>
# include <__cstddef/size_t.h>
# include <__functional/hash.h>
+# include <__functional/is_transparent.h>
# include <__functional/unary_function.h>
# include <__fwd/ostream.h>
# include <__fwd/string.h>
@@ -234,11 +235,13 @@ namespace std {
# include <__ranges/size.h>
# include <__string/char_traits.h>
# include <__type_traits/is_array.h>
+# include <__type_traits/desugars_to.h>
# include <__type_traits/is_convertible.h>
# include <__type_traits/is_same.h>
# include <__type_traits/is_standard_layout.h>
# include <__type_traits/is_trivially_constructible.h>
# include <__type_traits/is_trivially_copyable.h>
+# include <__type_traits/remove_cv.h>
# include <__type_traits/remove_cvref.h>
# include <__type_traits/remove_reference.h>
# include <__type_traits/type_identity.h>
@@ -933,6 +936,30 @@ template <>
struct hash<basic_string_view<wchar_t, char_traits<wchar_t> > > : __string_view_hash<wchar_t> {};
# endif
+template <class _Comparator, class _CharT2, class _CharT, class _Traits>
+inline const bool __is_transparently_comparable_v<_Comparator, basic_string_view<_CharT, _Traits>, _CharT2*> =
+ is_same<_CharT, __remove_cv_t<_CharT2> >::value &&
+ (__desugars_to_v<__less_tag,
+ _Comparator,
+ basic_string_view<_CharT, _Traits>,
+ basic_string_view<_CharT, _Traits> > ||
+ __desugars_to_v<__greater_tag,
+ _Comparator,
+ basic_string_view<_CharT, _Traits>,
+ basic_string_view<_CharT, _Traits> >);
+
+template <class _Comparator, class _CharT2, class _CharT, class _Traits>
+inline const bool __is_transparently_comparable_v<_Comparator, _CharT2*, basic_string_view<_CharT, _Traits> > =
+ __is_transparently_comparable_v<_Comparator, basic_string_view<_CharT, _Traits>, _CharT2*>;
+
+template <class _Comparator, class _CharT, class _Traits, size_t _Np>
+inline const bool __is_transparently_comparable_v<_Comparator, basic_string_view<_CharT, _Traits>, _CharT[_Np]> =
+ __is_transparently_comparable_v<_Comparator, basic_string_view<_CharT, _Traits>, const _CharT*>;
+
+template <class _Comparator, class _CharT, class _Traits, size_t _Np>
+inline const bool __is_transparently_comparable_v<_Comparator, _CharT[_Np], basic_string_view<_CharT, _Traits> > =
+ __is_transparently_comparable_v<_Comparator, basic_string_view<_CharT, _Traits>, const _CharT*>;
+
# if _LIBCPP_STD_VER >= 14
inline namespace literals {
inline namespace string_view_literals {
diff --git a/libcxx/test/libcxx/type_traits/is_transparently_comparable.compile.pass.cpp b/libcxx/test/libcxx/type_traits/is_transparently_comparable.compile.pass.cpp
index 8ab8cb0be8418..870a77cf33abc 100644
--- a/libcxx/test/libcxx/type_traits/is_transparently_comparable.compile.pass.cpp
+++ b/libcxx/test/libcxx/type_traits/is_transparently_comparable.compile.pass.cpp
@@ -10,8 +10,10 @@
#include <functional>
#include <string>
+#include <string_view>
#include <__type_traits/desugars_to.h>
+// basic_string with char pointers/arrays
static_assert(std::__is_transparently_comparable_v<std::less<std::string>, std::string, const char*>, "");
static_assert(std::__is_transparently_comparable_v<std::less<std::string>, std::string, char*>, "");
static_assert(std::__is_transparently_comparable_v<std::less<std::string>, std::string, char[5]>, "");
@@ -30,3 +32,29 @@ static_assert(
static_assert(
!std::__is_transparently_comparable_v<std::less<std::reference_wrapper<std::string> >, char const*, std::string>,
"");
+
+// basic_string_view with char pointers/arrays
+static_assert(
+ std::__is_transparently_comparable_v<std::less<std::string_view>, std::string_view, const char*>, "");
+static_assert(std::__is_transparently_comparable_v<std::less<std::string_view>, std::string_view, char*>, "");
+static_assert(std::__is_transparently_comparable_v<std::less<std::string_view>, std::string_view, char[5]>, "");
+
+static_assert(
+ std::__is_transparently_comparable_v<std::less<std::string_view>, const char*, std::string_view>, "");
+static_assert(std::__is_transparently_comparable_v<std::less<std::string_view>, char*, std::string_view>, "");
+static_assert(std::__is_transparently_comparable_v<std::less<std::string_view>, char[5], std::string_view>, "");
+
+static_assert(
+ !std::__is_transparently_comparable_v<std::less<std::reference_wrapper<std::string_view> >,
+ std::string_view,
+ const char*>,
+ "");
+static_assert(
+ !std::__is_transparently_comparable_v<std::less<std::reference_wrapper<std::string_view> >,
+ const char*,
+ std::string_view>,
+ "");
+
+// Cross-type: basic_string key with basic_string_view argument
+static_assert(std::__is_transparently_comparable_v<std::less<std::string>, std::string, std::string_view>, "");
+static_assert(std::__is_transparently_comparable_v<std::less<std::string>, std::string_view, std::string>, "");
>From e803867b437041b50e15d2f2fdabfe2a72a74a81 Mon Sep 17 00:00:00 2001
From: mlevine55 <mlevine55 at bloomberg.net>
Date: Tue, 5 May 2026 12:25:09 -0400
Subject: [PATCH 02/10] clang-format
Signed-off-by: mlevine55 <mlevine55 at bloomberg.net>
---
..._transparently_comparable.compile.pass.cpp | 24 ++++++++-----------
1 file changed, 10 insertions(+), 14 deletions(-)
diff --git a/libcxx/test/libcxx/type_traits/is_transparently_comparable.compile.pass.cpp b/libcxx/test/libcxx/type_traits/is_transparently_comparable.compile.pass.cpp
index 870a77cf33abc..d02f5713ce777 100644
--- a/libcxx/test/libcxx/type_traits/is_transparently_comparable.compile.pass.cpp
+++ b/libcxx/test/libcxx/type_traits/is_transparently_comparable.compile.pass.cpp
@@ -34,26 +34,22 @@ static_assert(
"");
// basic_string_view with char pointers/arrays
-static_assert(
- std::__is_transparently_comparable_v<std::less<std::string_view>, std::string_view, const char*>, "");
+static_assert(std::__is_transparently_comparable_v<std::less<std::string_view>, std::string_view, const char*>, "");
static_assert(std::__is_transparently_comparable_v<std::less<std::string_view>, std::string_view, char*>, "");
static_assert(std::__is_transparently_comparable_v<std::less<std::string_view>, std::string_view, char[5]>, "");
-static_assert(
- std::__is_transparently_comparable_v<std::less<std::string_view>, const char*, std::string_view>, "");
+static_assert(std::__is_transparently_comparable_v<std::less<std::string_view>, const char*, std::string_view>, "");
static_assert(std::__is_transparently_comparable_v<std::less<std::string_view>, char*, std::string_view>, "");
static_assert(std::__is_transparently_comparable_v<std::less<std::string_view>, char[5], std::string_view>, "");
-static_assert(
- !std::__is_transparently_comparable_v<std::less<std::reference_wrapper<std::string_view> >,
- std::string_view,
- const char*>,
- "");
-static_assert(
- !std::__is_transparently_comparable_v<std::less<std::reference_wrapper<std::string_view> >,
- const char*,
- std::string_view>,
- "");
+static_assert(!std::__is_transparently_comparable_v<std::less<std::reference_wrapper<std::string_view> >,
+ std::string_view,
+ const char*>,
+ "");
+static_assert(!std::__is_transparently_comparable_v<std::less<std::reference_wrapper<std::string_view> >,
+ const char*,
+ std::string_view>,
+ "");
// Cross-type: basic_string key with basic_string_view argument
static_assert(std::__is_transparently_comparable_v<std::less<std::string>, std::string, std::string_view>, "");
>From 19ff6be3b4f2b26bced5d8df434574906875d0f0 Mon Sep 17 00:00:00 2001
From: mlevine55 <mlevine55 at bloomberg.net>
Date: Tue, 5 May 2026 12:29:15 -0400
Subject: [PATCH 03/10] fix indendating and include ordering
Signed-off-by: mlevine55 <mlevine55 at bloomberg.net>
---
libcxx/include/string | 34 ++++++++++++++++------------------
libcxx/include/string_view | 2 +-
2 files changed, 17 insertions(+), 19 deletions(-)
diff --git a/libcxx/include/string b/libcxx/include/string
index 82deee9085027..1b3459752a88c 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -2600,27 +2600,25 @@ inline const bool __is_transparently_comparable_v<_Comparator, _CharT[_Np], basi
__is_transparently_comparable_v<_Comparator, basic_string<_CharT, _Traits, _Alloc>, const _CharT*>;
template <class _Comparator, class _CharT, class _Traits, class _Alloc>
-inline const bool
- __is_transparently_comparable_v<_Comparator,
- basic_string<_CharT, _Traits, _Alloc>,
- basic_string_view<_CharT, _Traits> > =
- __desugars_to_v<__less_tag,
- _Comparator,
- basic_string<_CharT, _Traits, _Alloc>,
- basic_string<_CharT, _Traits, _Alloc> > ||
- __desugars_to_v<__greater_tag,
- _Comparator,
- basic_string<_CharT, _Traits, _Alloc>,
- basic_string<_CharT, _Traits, _Alloc> >;
+inline const bool __is_transparently_comparable_v<_Comparator,
+ basic_string<_CharT, _Traits, _Alloc>,
+ basic_string_view<_CharT, _Traits> > =
+ __desugars_to_v<__less_tag,
+ _Comparator,
+ basic_string<_CharT, _Traits, _Alloc>,
+ basic_string<_CharT, _Traits, _Alloc> > ||
+ __desugars_to_v<__greater_tag,
+ _Comparator,
+ basic_string<_CharT, _Traits, _Alloc>,
+ basic_string<_CharT, _Traits, _Alloc> >;
template <class _Comparator, class _CharT, class _Traits, class _Alloc>
-inline const bool
+inline const bool __is_transparently_comparable_v<_Comparator,
+ basic_string_view<_CharT, _Traits>,
+ basic_string<_CharT, _Traits, _Alloc> > =
__is_transparently_comparable_v<_Comparator,
- basic_string_view<_CharT, _Traits>,
- basic_string<_CharT, _Traits, _Alloc> > =
- __is_transparently_comparable_v<_Comparator,
- basic_string<_CharT, _Traits, _Alloc>,
- basic_string_view<_CharT, _Traits> >;
+ basic_string<_CharT, _Traits, _Alloc>,
+ basic_string_view<_CharT, _Traits> >;
# if _LIBCPP_STD_VER >= 17
template <class _InputIterator,
diff --git a/libcxx/include/string_view b/libcxx/include/string_view
index 0ea8b5f3965f5..6dc3af94bb99f 100644
--- a/libcxx/include/string_view
+++ b/libcxx/include/string_view
@@ -234,8 +234,8 @@ namespace std {
# include <__ranges/enable_view.h>
# include <__ranges/size.h>
# include <__string/char_traits.h>
-# include <__type_traits/is_array.h>
# include <__type_traits/desugars_to.h>
+# include <__type_traits/is_array.h>
# include <__type_traits/is_convertible.h>
# include <__type_traits/is_same.h>
# include <__type_traits/is_standard_layout.h>
>From 45689c31a8987a44112a294d5d91c5642bb0843f Mon Sep 17 00:00:00 2001
From: mlevine55 <mlevine55 at bloomberg.net>
Date: Tue, 5 May 2026 13:06:13 -0400
Subject: [PATCH 04/10] added tests and benchmarks
Signed-off-by: mlevine55 <mlevine55 at bloomberg.net>
---
.../containers/associative/map.bench.cpp | 37 ++++++++++++
...kup_with_transparently_comparable.pass.cpp | 57 +++++++++++++++++++
2 files changed, 94 insertions(+)
diff --git a/libcxx/test/benchmarks/containers/associative/map.bench.cpp b/libcxx/test/benchmarks/containers/associative/map.bench.cpp
index cc9ffd857caf2..0228136875fb4 100644
--- a/libcxx/test/benchmarks/containers/associative/map.bench.cpp
+++ b/libcxx/test/benchmarks/containers/associative/map.bench.cpp
@@ -10,6 +10,7 @@
#include <map>
#include <string>
+#include <string_view>
#include <utility>
#include "associative_container_benchmarks.h"
@@ -29,6 +30,42 @@ static void BM_map_find_string_literal(benchmark::State& state) {
BENCHMARK(BM_map_find_string_literal);
+// Benchmark: find() with string_view vs find() with string (constructed from
+// string_view). Demonstrates the benefit of __is_transparently_comparable_v
+// for basic_string_view: the optimized path avoids constructing a temporary
+// std::string (and its potential heap allocation for keys beyond SSO).
+
+static void BM_map_find_string_view(benchmark::State& state) {
+ std::map<std::string, int> map;
+ map.emplace("Something very very long to show a long string situation", 1);
+ map.emplace("Something Else", 2);
+
+ std::string_view sv = "Something very very long to show a long string situation";
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(map);
+ benchmark::DoNotOptimize(map.find(sv));
+ }
+}
+
+BENCHMARK(BM_map_find_string_view);
+
+static void BM_map_find_string_constructed_from_view(benchmark::State& state) {
+ std::map<std::string, int> map;
+ map.emplace("Something very very long to show a long string situation", 1);
+ map.emplace("Something Else", 2);
+
+ std::string_view sv = "Something very very long to show a long string situation";
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(map);
+ // Simulates the non-optimized path: explicitly construct a std::string
+ benchmark::DoNotOptimize(map.find(std::string(sv)));
+ }
+}
+
+BENCHMARK(BM_map_find_string_constructed_from_view);
+
template <class K, class V>
struct support::adapt_operations<std::map<K, V>> {
using ValueType = typename std::map<K, V>::value_type;
diff --git a/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp b/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp
index 219df94168b55..fef6b91ab5166 100644
--- a/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp
+++ b/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp
@@ -20,6 +20,7 @@
#include <map>
#include <set>
#include <string>
+#include <string_view>
#include "count_new.h"
#include "test_macros.h"
@@ -140,5 +141,61 @@ int main(int, char**) {
}
}
+ // string_view lookups should also not allocate (tests the new string_view
+ // __is_transparently_comparable_v specializations)
+ {
+ std::map<std::string, int> c;
+ std::string_view key = "long-string-to-exceed-SSO-buffer";
+ c[std::string(key)] = 1;
+
+ // find
+ {
+ globalMemCounter.reset();
+ (void)c.find(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
+ {
+ const std::map<std::string, int>& cc = c;
+ globalMemCounter.reset();
+ (void)cc.find(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
+
+#if TEST_STD_VER >= 20
+ // contains
+ {
+ globalMemCounter.reset();
+ (void)c.contains(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
+#endif
+
+ // at
+ {
+ globalMemCounter.reset();
+ (void)c.at(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
+ {
+ const std::map<std::string, int>& cc = c;
+ globalMemCounter.reset();
+ (void)cc.at(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
+ }
+
+ // string_view lookups with std::greater
+ {
+ std::map<std::string, int, std::greater<std::string>> c;
+ std::string_view key = "long-string-to-exceed-SSO-buffer";
+ c[std::string(key)] = 1;
+
+ {
+ globalMemCounter.reset();
+ (void)c.find(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
+ }
+
return 0;
}
>From 425a086788c9ea5ffb42078a2edb36a4db5f7b8e Mon Sep 17 00:00:00 2001
From: mlevine55 <mlevine55 at bloomberg.net>
Date: Wed, 6 May 2026 13:42:26 -0400
Subject: [PATCH 05/10] add more compile time tests
Signed-off-by: mlevine55 <mlevine55 at bloomberg.net>
---
..._transparently_comparable.compile.pass.cpp | 58 ++++++++++++++++++-
1 file changed, 55 insertions(+), 3 deletions(-)
diff --git a/libcxx/test/libcxx/type_traits/is_transparently_comparable.compile.pass.cpp b/libcxx/test/libcxx/type_traits/is_transparently_comparable.compile.pass.cpp
index d02f5713ce777..70d514a1a1e69 100644
--- a/libcxx/test/libcxx/type_traits/is_transparently_comparable.compile.pass.cpp
+++ b/libcxx/test/libcxx/type_traits/is_transparently_comparable.compile.pass.cpp
@@ -13,7 +13,7 @@
#include <string_view>
#include <__type_traits/desugars_to.h>
-// basic_string with char pointers/arrays
+// basic_string with char pointers/arrays (std::less)
static_assert(std::__is_transparently_comparable_v<std::less<std::string>, std::string, const char*>, "");
static_assert(std::__is_transparently_comparable_v<std::less<std::string>, std::string, char*>, "");
static_assert(std::__is_transparently_comparable_v<std::less<std::string>, std::string, char[5]>, "");
@@ -22,6 +22,16 @@ static_assert(std::__is_transparently_comparable_v<std::less<std::string>, const
static_assert(std::__is_transparently_comparable_v<std::less<std::string>, char*, std::string>, "");
static_assert(std::__is_transparently_comparable_v<std::less<std::string>, char[5], std::string>, "");
+// basic_string with char pointers/arrays (std::greater)
+static_assert(std::__is_transparently_comparable_v<std::greater<std::string>, std::string, const char*>, "");
+static_assert(std::__is_transparently_comparable_v<std::greater<std::string>, std::string, char*>, "");
+static_assert(std::__is_transparently_comparable_v<std::greater<std::string>, std::string, char[5]>, "");
+
+static_assert(std::__is_transparently_comparable_v<std::greater<std::string>, const char*, std::string>, "");
+static_assert(std::__is_transparently_comparable_v<std::greater<std::string>, char*, std::string>, "");
+static_assert(std::__is_transparently_comparable_v<std::greater<std::string>, char[5], std::string>, "");
+
+// basic_string negative tests: wrong comparator
static_assert(
!std::__is_transparently_comparable_v<std::less<std::reference_wrapper<std::string> >, std::string, char[5]>, "");
static_assert(
@@ -33,7 +43,11 @@ static_assert(
!std::__is_transparently_comparable_v<std::less<std::reference_wrapper<std::string> >, char const*, std::string>,
"");
-// basic_string_view with char pointers/arrays
+// basic_string negative tests: mismatched character type
+static_assert(!std::__is_transparently_comparable_v<std::less<std::string>, std::string, const wchar_t*>, "");
+static_assert(!std::__is_transparently_comparable_v<std::less<std::string>, const wchar_t*, std::string>, "");
+
+// basic_string_view with char pointers/arrays (std::less)
static_assert(std::__is_transparently_comparable_v<std::less<std::string_view>, std::string_view, const char*>, "");
static_assert(std::__is_transparently_comparable_v<std::less<std::string_view>, std::string_view, char*>, "");
static_assert(std::__is_transparently_comparable_v<std::less<std::string_view>, std::string_view, char[5]>, "");
@@ -42,6 +56,18 @@ static_assert(std::__is_transparently_comparable_v<std::less<std::string_view>,
static_assert(std::__is_transparently_comparable_v<std::less<std::string_view>, char*, std::string_view>, "");
static_assert(std::__is_transparently_comparable_v<std::less<std::string_view>, char[5], std::string_view>, "");
+// basic_string_view with char pointers/arrays (std::greater)
+static_assert(
+ std::__is_transparently_comparable_v<std::greater<std::string_view>, std::string_view, const char*>, "");
+static_assert(std::__is_transparently_comparable_v<std::greater<std::string_view>, std::string_view, char*>, "");
+static_assert(std::__is_transparently_comparable_v<std::greater<std::string_view>, std::string_view, char[5]>, "");
+
+static_assert(
+ std::__is_transparently_comparable_v<std::greater<std::string_view>, const char*, std::string_view>, "");
+static_assert(std::__is_transparently_comparable_v<std::greater<std::string_view>, char*, std::string_view>, "");
+static_assert(std::__is_transparently_comparable_v<std::greater<std::string_view>, char[5], std::string_view>, "");
+
+// basic_string_view negative tests: wrong comparator
static_assert(!std::__is_transparently_comparable_v<std::less<std::reference_wrapper<std::string_view> >,
std::string_view,
const char*>,
@@ -50,7 +76,33 @@ static_assert(!std::__is_transparently_comparable_v<std::less<std::reference_wra
const char*,
std::string_view>,
"");
+static_assert(!std::__is_transparently_comparable_v<std::less<std::reference_wrapper<std::string_view> >,
+ std::string_view,
+ char[5]>,
+ "");
+static_assert(!std::__is_transparently_comparable_v<std::less<std::reference_wrapper<std::string_view> >,
+ char[5],
+ std::string_view>,
+ "");
-// Cross-type: basic_string key with basic_string_view argument
+// basic_string_view negative tests: mismatched character type
+static_assert(!std::__is_transparently_comparable_v<std::less<std::string_view>, std::string_view, const wchar_t*>, "");
+static_assert(!std::__is_transparently_comparable_v<std::less<std::string_view>, const wchar_t*, std::string_view>, "");
+
+// Cross-type: basic_string key with basic_string_view argument (std::less)
static_assert(std::__is_transparently_comparable_v<std::less<std::string>, std::string, std::string_view>, "");
static_assert(std::__is_transparently_comparable_v<std::less<std::string>, std::string_view, std::string>, "");
+
+// Cross-type: basic_string key with basic_string_view argument (std::greater)
+static_assert(std::__is_transparently_comparable_v<std::greater<std::string>, std::string, std::string_view>, "");
+static_assert(std::__is_transparently_comparable_v<std::greater<std::string>, std::string_view, std::string>, "");
+
+// Cross-type negative: wrong comparator
+static_assert(!std::__is_transparently_comparable_v<std::less<std::reference_wrapper<std::string> >,
+ std::string,
+ std::string_view>,
+ "");
+static_assert(!std::__is_transparently_comparable_v<std::less<std::reference_wrapper<std::string> >,
+ std::string_view,
+ std::string>,
+ "");
>From 1bda000f1b01e9ee89ba304573e5997168a3eaab Mon Sep 17 00:00:00 2001
From: mlevine55 <mlevine55 at bloomberg.net>
Date: Wed, 6 May 2026 13:54:05 -0400
Subject: [PATCH 06/10] added more lookup tests
Signed-off-by: mlevine55 <mlevine55 at bloomberg.net>
---
...kup_with_transparently_comparable.pass.cpp | 41 +++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp b/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp
index fef6b91ab5166..b6c81006850ee 100644
--- a/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp
+++ b/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp
@@ -168,6 +168,12 @@ int main(int, char**) {
(void)c.contains(key);
assert(globalMemCounter.checkNewCalledEq(0));
}
+ {
+ const std::map<std::string, int>& cc = c;
+ globalMemCounter.reset();
+ (void)cc.contains(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
#endif
// at
@@ -190,11 +196,46 @@ int main(int, char**) {
std::string_view key = "long-string-to-exceed-SSO-buffer";
c[std::string(key)] = 1;
+ // find
{
globalMemCounter.reset();
(void)c.find(key);
assert(globalMemCounter.checkNewCalledEq(0));
}
+ {
+ const std::map<std::string, int, std::greater<std::string>>& cc = c;
+ globalMemCounter.reset();
+ (void)cc.find(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
+
+#if TEST_STD_VER >= 20
+ // contains
+ {
+ globalMemCounter.reset();
+ (void)c.contains(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
+ {
+ const std::map<std::string, int, std::greater<std::string>>& cc = c;
+ globalMemCounter.reset();
+ (void)cc.contains(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
+#endif
+
+ // at
+ {
+ globalMemCounter.reset();
+ (void)c.at(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
+ {
+ const std::map<std::string, int, std::greater<std::string>>& cc = c;
+ globalMemCounter.reset();
+ (void)cc.at(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
}
return 0;
>From 60365d9f0e40b3883b42ddbaba2aee81442fdd92 Mon Sep 17 00:00:00 2001
From: mlevine55 <mlevine55 at bloomberg.net>
Date: Wed, 6 May 2026 14:10:59 -0400
Subject: [PATCH 07/10] added more tests and benchmarks
Signed-off-by: mlevine55 <mlevine55 at bloomberg.net>
---
.../containers/associative/map.bench.cpp | 118 ++++++++++++++++--
...kup_with_transparently_comparable.pass.cpp | 52 ++++++++
2 files changed, 161 insertions(+), 9 deletions(-)
diff --git a/libcxx/test/benchmarks/containers/associative/map.bench.cpp b/libcxx/test/benchmarks/containers/associative/map.bench.cpp
index 0228136875fb4..07bde13d654d3 100644
--- a/libcxx/test/benchmarks/containers/associative/map.bench.cpp
+++ b/libcxx/test/benchmarks/containers/associative/map.bench.cpp
@@ -34,13 +34,21 @@ BENCHMARK(BM_map_find_string_literal);
// string_view). Demonstrates the benefit of __is_transparently_comparable_v
// for basic_string_view: the optimized path avoids constructing a temporary
// std::string (and its potential heap allocation for keys beyond SSO).
+// Compare BM_map_find_string_view against BM_map_find_string_constructed_from_view
+// in the output to see the speedup.
-static void BM_map_find_string_view(benchmark::State& state) {
+static constexpr const char* kLongKey = "Something very very long to show a long string situation";
+
+static std::map<std::string, int> make_test_map() {
std::map<std::string, int> map;
- map.emplace("Something very very long to show a long string situation", 1);
+ map.emplace(kLongKey, 1);
map.emplace("Something Else", 2);
+ return map;
+}
- std::string_view sv = "Something very very long to show a long string situation";
+static void BM_map_find_string_view(benchmark::State& state) {
+ auto map = make_test_map();
+ std::string_view sv = kLongKey;
for (auto _ : state) {
benchmark::DoNotOptimize(map);
@@ -51,21 +59,113 @@ static void BM_map_find_string_view(benchmark::State& state) {
BENCHMARK(BM_map_find_string_view);
static void BM_map_find_string_constructed_from_view(benchmark::State& state) {
- std::map<std::string, int> map;
- map.emplace("Something very very long to show a long string situation", 1);
- map.emplace("Something Else", 2);
-
- std::string_view sv = "Something very very long to show a long string situation";
+ auto map = make_test_map();
+ std::string_view sv = kLongKey;
for (auto _ : state) {
benchmark::DoNotOptimize(map);
- // Simulates the non-optimized path: explicitly construct a std::string
benchmark::DoNotOptimize(map.find(std::string(sv)));
}
}
BENCHMARK(BM_map_find_string_constructed_from_view);
+static void BM_map_contains_string_view(benchmark::State& state) {
+ auto map = make_test_map();
+ std::string_view sv = kLongKey;
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(map);
+ benchmark::DoNotOptimize(map.contains(sv));
+ }
+}
+
+BENCHMARK(BM_map_contains_string_view);
+
+static void BM_map_contains_string_constructed_from_view(benchmark::State& state) {
+ auto map = make_test_map();
+ std::string_view sv = kLongKey;
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(map);
+ benchmark::DoNotOptimize(map.contains(std::string(sv)));
+ }
+}
+
+BENCHMARK(BM_map_contains_string_constructed_from_view);
+
+static void BM_map_at_string_view(benchmark::State& state) {
+ auto map = make_test_map();
+ std::string_view sv = kLongKey;
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(map);
+ benchmark::DoNotOptimize(map.at(sv));
+ }
+}
+
+BENCHMARK(BM_map_at_string_view);
+
+static void BM_map_at_string_constructed_from_view(benchmark::State& state) {
+ auto map = make_test_map();
+ std::string_view sv = kLongKey;
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(map);
+ benchmark::DoNotOptimize(map.at(std::string(sv)));
+ }
+}
+
+BENCHMARK(BM_map_at_string_constructed_from_view);
+
+static void BM_map_lower_bound_string_view(benchmark::State& state) {
+ auto map = make_test_map();
+ std::string_view sv = kLongKey;
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(map);
+ benchmark::DoNotOptimize(map.lower_bound(sv));
+ }
+}
+
+BENCHMARK(BM_map_lower_bound_string_view);
+
+static void BM_map_lower_bound_string_constructed_from_view(benchmark::State& state) {
+ auto map = make_test_map();
+ std::string_view sv = kLongKey;
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(map);
+ benchmark::DoNotOptimize(map.lower_bound(std::string(sv)));
+ }
+}
+
+BENCHMARK(BM_map_lower_bound_string_constructed_from_view);
+
+static void BM_map_upper_bound_string_view(benchmark::State& state) {
+ auto map = make_test_map();
+ std::string_view sv = kLongKey;
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(map);
+ benchmark::DoNotOptimize(map.upper_bound(sv));
+ }
+}
+
+BENCHMARK(BM_map_upper_bound_string_view);
+
+static void BM_map_upper_bound_string_constructed_from_view(benchmark::State& state) {
+ auto map = make_test_map();
+ std::string_view sv = kLongKey;
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(map);
+ benchmark::DoNotOptimize(map.upper_bound(std::string(sv)));
+ }
+}
+
+BENCHMARK(BM_map_upper_bound_string_constructed_from_view);
+
template <class K, class V>
struct support::adapt_operations<std::map<K, V>> {
using ValueType = typename std::map<K, V>::value_type;
diff --git a/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp b/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp
index b6c81006850ee..afbefc1a14082 100644
--- a/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp
+++ b/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp
@@ -188,6 +188,32 @@ int main(int, char**) {
(void)cc.at(key);
assert(globalMemCounter.checkNewCalledEq(0));
}
+
+ // lower_bound
+ {
+ globalMemCounter.reset();
+ (void)c.lower_bound(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
+ {
+ const std::map<std::string, int>& cc = c;
+ globalMemCounter.reset();
+ (void)cc.lower_bound(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
+
+ // upper_bound
+ {
+ globalMemCounter.reset();
+ (void)c.upper_bound(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
+ {
+ const std::map<std::string, int>& cc = c;
+ globalMemCounter.reset();
+ (void)cc.upper_bound(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
}
// string_view lookups with std::greater
@@ -236,6 +262,32 @@ int main(int, char**) {
(void)cc.at(key);
assert(globalMemCounter.checkNewCalledEq(0));
}
+
+ // lower_bound
+ {
+ globalMemCounter.reset();
+ (void)c.lower_bound(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
+ {
+ const std::map<std::string, int, std::greater<std::string>>& cc = c;
+ globalMemCounter.reset();
+ (void)cc.lower_bound(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
+
+ // upper_bound
+ {
+ globalMemCounter.reset();
+ (void)c.upper_bound(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
+ {
+ const std::map<std::string, int, std::greater<std::string>>& cc = c;
+ globalMemCounter.reset();
+ (void)cc.upper_bound(key);
+ assert(globalMemCounter.checkNewCalledEq(0));
+ }
}
return 0;
>From 73df0721a014b8346f591180b854fc1c46cf623f Mon Sep 17 00:00:00 2001
From: mlevine55 <mlevine55 at bloomberg.net>
Date: Wed, 6 May 2026 14:15:43 -0400
Subject: [PATCH 08/10] fixed formatting
Signed-off-by: mlevine55 <mlevine55 at bloomberg.net>
---
..._transparently_comparable.compile.pass.cpp | 22 +++++++++----------
1 file changed, 10 insertions(+), 12 deletions(-)
diff --git a/libcxx/test/libcxx/type_traits/is_transparently_comparable.compile.pass.cpp b/libcxx/test/libcxx/type_traits/is_transparently_comparable.compile.pass.cpp
index 70d514a1a1e69..f2d8622d219ee 100644
--- a/libcxx/test/libcxx/type_traits/is_transparently_comparable.compile.pass.cpp
+++ b/libcxx/test/libcxx/type_traits/is_transparently_comparable.compile.pass.cpp
@@ -57,13 +57,11 @@ static_assert(std::__is_transparently_comparable_v<std::less<std::string_view>,
static_assert(std::__is_transparently_comparable_v<std::less<std::string_view>, char[5], std::string_view>, "");
// basic_string_view with char pointers/arrays (std::greater)
-static_assert(
- std::__is_transparently_comparable_v<std::greater<std::string_view>, std::string_view, const char*>, "");
+static_assert(std::__is_transparently_comparable_v<std::greater<std::string_view>, std::string_view, const char*>, "");
static_assert(std::__is_transparently_comparable_v<std::greater<std::string_view>, std::string_view, char*>, "");
static_assert(std::__is_transparently_comparable_v<std::greater<std::string_view>, std::string_view, char[5]>, "");
-static_assert(
- std::__is_transparently_comparable_v<std::greater<std::string_view>, const char*, std::string_view>, "");
+static_assert(std::__is_transparently_comparable_v<std::greater<std::string_view>, const char*, std::string_view>, "");
static_assert(std::__is_transparently_comparable_v<std::greater<std::string_view>, char*, std::string_view>, "");
static_assert(std::__is_transparently_comparable_v<std::greater<std::string_view>, char[5], std::string_view>, "");
@@ -98,11 +96,11 @@ static_assert(std::__is_transparently_comparable_v<std::greater<std::string>, st
static_assert(std::__is_transparently_comparable_v<std::greater<std::string>, std::string_view, std::string>, "");
// Cross-type negative: wrong comparator
-static_assert(!std::__is_transparently_comparable_v<std::less<std::reference_wrapper<std::string> >,
- std::string,
- std::string_view>,
- "");
-static_assert(!std::__is_transparently_comparable_v<std::less<std::reference_wrapper<std::string> >,
- std::string_view,
- std::string>,
- "");
+static_assert(
+ !std::
+ __is_transparently_comparable_v<std::less<std::reference_wrapper<std::string> >, std::string, std::string_view>,
+ "");
+static_assert(
+ !std::
+ __is_transparently_comparable_v<std::less<std::reference_wrapper<std::string> >, std::string_view, std::string>,
+ "");
>From cbf3f790872dec67bd3b95df4969716f7a89052d Mon Sep 17 00:00:00 2001
From: mlevine55 <mlevine55 at bloomberg.net>
Date: Wed, 6 May 2026 14:46:51 -0400
Subject: [PATCH 09/10] removed the lower_bound and upper_bound
Signed-off-by: mlevine55 <mlevine55 at bloomberg.net>
---
.../containers/associative/map.bench.cpp | 48 ------------------
...kup_with_transparently_comparable.pass.cpp | 50 -------------------
2 files changed, 98 deletions(-)
diff --git a/libcxx/test/benchmarks/containers/associative/map.bench.cpp b/libcxx/test/benchmarks/containers/associative/map.bench.cpp
index 07bde13d654d3..5c2f2ba07dbda 100644
--- a/libcxx/test/benchmarks/containers/associative/map.bench.cpp
+++ b/libcxx/test/benchmarks/containers/associative/map.bench.cpp
@@ -118,54 +118,6 @@ static void BM_map_at_string_constructed_from_view(benchmark::State& state) {
BENCHMARK(BM_map_at_string_constructed_from_view);
-static void BM_map_lower_bound_string_view(benchmark::State& state) {
- auto map = make_test_map();
- std::string_view sv = kLongKey;
-
- for (auto _ : state) {
- benchmark::DoNotOptimize(map);
- benchmark::DoNotOptimize(map.lower_bound(sv));
- }
-}
-
-BENCHMARK(BM_map_lower_bound_string_view);
-
-static void BM_map_lower_bound_string_constructed_from_view(benchmark::State& state) {
- auto map = make_test_map();
- std::string_view sv = kLongKey;
-
- for (auto _ : state) {
- benchmark::DoNotOptimize(map);
- benchmark::DoNotOptimize(map.lower_bound(std::string(sv)));
- }
-}
-
-BENCHMARK(BM_map_lower_bound_string_constructed_from_view);
-
-static void BM_map_upper_bound_string_view(benchmark::State& state) {
- auto map = make_test_map();
- std::string_view sv = kLongKey;
-
- for (auto _ : state) {
- benchmark::DoNotOptimize(map);
- benchmark::DoNotOptimize(map.upper_bound(sv));
- }
-}
-
-BENCHMARK(BM_map_upper_bound_string_view);
-
-static void BM_map_upper_bound_string_constructed_from_view(benchmark::State& state) {
- auto map = make_test_map();
- std::string_view sv = kLongKey;
-
- for (auto _ : state) {
- benchmark::DoNotOptimize(map);
- benchmark::DoNotOptimize(map.upper_bound(std::string(sv)));
- }
-}
-
-BENCHMARK(BM_map_upper_bound_string_constructed_from_view);
-
template <class K, class V>
struct support::adapt_operations<std::map<K, V>> {
using ValueType = typename std::map<K, V>::value_type;
diff --git a/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp b/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp
index afbefc1a14082..4f96b1e306716 100644
--- a/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp
+++ b/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp
@@ -189,31 +189,6 @@ int main(int, char**) {
assert(globalMemCounter.checkNewCalledEq(0));
}
- // lower_bound
- {
- globalMemCounter.reset();
- (void)c.lower_bound(key);
- assert(globalMemCounter.checkNewCalledEq(0));
- }
- {
- const std::map<std::string, int>& cc = c;
- globalMemCounter.reset();
- (void)cc.lower_bound(key);
- assert(globalMemCounter.checkNewCalledEq(0));
- }
-
- // upper_bound
- {
- globalMemCounter.reset();
- (void)c.upper_bound(key);
- assert(globalMemCounter.checkNewCalledEq(0));
- }
- {
- const std::map<std::string, int>& cc = c;
- globalMemCounter.reset();
- (void)cc.upper_bound(key);
- assert(globalMemCounter.checkNewCalledEq(0));
- }
}
// string_view lookups with std::greater
@@ -263,31 +238,6 @@ int main(int, char**) {
assert(globalMemCounter.checkNewCalledEq(0));
}
- // lower_bound
- {
- globalMemCounter.reset();
- (void)c.lower_bound(key);
- assert(globalMemCounter.checkNewCalledEq(0));
- }
- {
- const std::map<std::string, int, std::greater<std::string>>& cc = c;
- globalMemCounter.reset();
- (void)cc.lower_bound(key);
- assert(globalMemCounter.checkNewCalledEq(0));
- }
-
- // upper_bound
- {
- globalMemCounter.reset();
- (void)c.upper_bound(key);
- assert(globalMemCounter.checkNewCalledEq(0));
- }
- {
- const std::map<std::string, int, std::greater<std::string>>& cc = c;
- globalMemCounter.reset();
- (void)cc.upper_bound(key);
- assert(globalMemCounter.checkNewCalledEq(0));
- }
}
return 0;
>From 8b508e72be407718669f34578965d51e61b0dd36 Mon Sep 17 00:00:00 2001
From: mlevine55 <mlevine55 at bloomberg.net>
Date: Wed, 6 May 2026 14:53:17 -0400
Subject: [PATCH 10/10] fixed formatting
Signed-off-by: mlevine55 <mlevine55 at bloomberg.net>
---
.../associative/lookup_with_transparently_comparable.pass.cpp | 2 --
1 file changed, 2 deletions(-)
diff --git a/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp b/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp
index 4f96b1e306716..b6c81006850ee 100644
--- a/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp
+++ b/libcxx/test/libcxx/containers/associative/lookup_with_transparently_comparable.pass.cpp
@@ -188,7 +188,6 @@ int main(int, char**) {
(void)cc.at(key);
assert(globalMemCounter.checkNewCalledEq(0));
}
-
}
// string_view lookups with std::greater
@@ -237,7 +236,6 @@ int main(int, char**) {
(void)cc.at(key);
assert(globalMemCounter.checkNewCalledEq(0));
}
-
}
return 0;
More information about the libcxx-commits
mailing list