[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
Tue May 5 08:25:46 PDT 2026
https://github.com/levinem created https://github.com/llvm/llvm-project/pull/195871
### Problem
`__is_transparently_comparable_v` enables heterogeneous lookup in ordered containers (`map`, `multimap`) without requiring users to opt in with `std::less<>`. Currently, `basic_string` has specializations for transparent comparison with `const char*` and `char[N]`, but `basic_string_view` has none.
This means:
- `map<string, V>::find(string_view)` constructs a **temporary `std::string`** from the `string_view` argument, potentially triggering a heap allocation, just to perform the comparison
- `map<string_view, V>::find("literal")` constructs a temporary `string_view` (calling `strlen`) instead of comparing directly
These unnecessary temporaries are especially costly in hot paths like config lookups or symbol table queries with strings exceeding SSO size (~22 chars).
### Solution
Add 6 new partial specializations of `__is_transparently_comparable_v`:
**In `<string_view>`** — `string_view` key with `const char*`/`char[N]` (mirrors existing `basic_string` pattern):
1. `basic_string_view<C,T>` vs `CharT2*`
2. `CharT2*` vs `basic_string_view<C,T>` (reverse)
3. `basic_string_view<C,T>` vs `CharT[N]`
4. `CharT[N]` vs `basic_string_view<C,T>` (reverse)
**In `<string>`** — cross-type `basic_string` key with `basic_string_view` argument:
5. `basic_string<C,T,A>` vs `basic_string_view<C,T>`
6. `basic_string_view<C,T>` vs `basic_string<C,T,A>` (reverse)
Each specialization follows the same safety contract as the existing `basic_string` ones: it verifies character type compatibility and
that the comparator desugars to `__less_tag` or `__greater_tag`. No new `__desugars_to_v` specializations are needed — the existing
generic specializations for `less<T>` and `greater<T>` already cover `string_view`.
This is a pure compile-time change with no ABI impact. The `__tree` comparison mechanism (`__as_transparent` / `__make_transparent`)
already handles the runtime dispatch correctly for these type combinations.
Assisted by: Claude Code
>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] 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>, "");
More information about the libcxx-commits
mailing list