On Fri, Dec 5, 2025 at 4:38 PM Jonathan Wakely <[email protected]> wrote:
> This implements the C++23 proposal P2404R3 "Move-only types for
> equality_comparable_with, totally_ordered_with, and
> three_way_comparable_with". As agreed with the maintainers of libc++ and
> MSVC STL, we treat this as a DR for C++20. It allows reasonable code to
> compile which wasn't originally allowed in C++20, and only affects some
> obscure subsumption cases for valid C++20 code.
>
> libstdc++-v3/ChangeLog:
>
> PR libstdc++/122946
> * include/bits/version.def (concepts): Set value to 202207.
> * include/bits/version.h: Regenerate.
> * include/std/concepts (__comparison_common_type_with_impl)
> (__comparison_common_type_with): New helper concepts.
> (equality_comparable_with): Use __comparison_common_type_with.
> * libsupc++/compare (three_way_comparable_with): Likewise.
> (__glibcxx_want_concepts): Define to get __cpp_lib_concepts
> here.
> * testsuite/std/concepts/concepts.compare/move_only.cc: New
> test.
> ---
>
> Tested x86_64-linux.
>
I needed to remind myself what the changes were from the paper. One
of these small change, big impact cases.
LGTM.
> If we're going to treat this as a DR for C++20 then we should ship it in
> GCC 16 with the -std=gnu++20 default, so that we don't introduce a
> change later which affects those obscure subsumption cases.
>
> libstdc++-v3/include/bits/version.def | 6 +++-
> libstdc++-v3/include/bits/version.h | 4 +--
> libstdc++-v3/include/std/concepts | 23 ++++++++++++++-
> libstdc++-v3/libsupc++/compare | 7 ++---
> .../concepts/concepts.compare/move_only.cc | 28 +++++++++++++++++++
> 5 files changed, 60 insertions(+), 8 deletions(-)
> create mode 100644
> libstdc++-v3/testsuite/std/concepts/concepts.compare/move_only.cc
>
> diff --git a/libstdc++-v3/include/bits/version.def
> b/libstdc++-v3/include/bits/version.def
> index d20e08519cac..071e78555e77 100644
> --- a/libstdc++-v3/include/bits/version.def
> +++ b/libstdc++-v3/include/bits/version.def
> @@ -879,8 +879,12 @@ ftms = {
>
> ftms = {
> name = concepts;
> + // 201806 P0898R3 Standard Library Concepts
> + // 201907 P1754R1 Rename concepts to standard_case for C++20
> + // 202002 P1964R2 Wording for boolean-testable
> + // 202207 P2404R3 Move-only types for equality_comparable_with, etc.
> values = {
> - v = 202002;
> + v = 202207;
> cxxmin = 20;
> extra_cond = "__cpp_concepts >= 201907L";
> };
> diff --git a/libstdc++-v3/include/bits/version.h
> b/libstdc++-v3/include/bits/version.h
> index c75368d44c29..58633522d602 100644
> --- a/libstdc++-v3/include/bits/version.h
> +++ b/libstdc++-v3/include/bits/version.h
> @@ -985,9 +985,9 @@
>
> #if !defined(__cpp_lib_concepts)
> # if (__cplusplus >= 202002L) && (__cpp_concepts >= 201907L)
> -# define __glibcxx_concepts 202002L
> +# define __glibcxx_concepts 202207L
> # if defined(__glibcxx_want_all) || defined(__glibcxx_want_concepts)
> -# define __cpp_lib_concepts 202002L
> +# define __cpp_lib_concepts 202207L
> # endif
> # endif
> #endif /* !defined(__cpp_lib_concepts) */
> diff --git a/libstdc++-v3/include/std/concepts
> b/libstdc++-v3/include/std/concepts
> index d9920a8f20a8..870b0a47eab1 100644
> --- a/libstdc++-v3/include/std/concepts
> +++ b/libstdc++-v3/include/std/concepts
> @@ -296,6 +296,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> { { !static_cast<_Tp&&>(__t) } -> __boolean_testable_impl; };
> } // namespace __detail
>
> + // [concept.comparisoncommontype], helpers for comparison common types
> + namespace __detail
> + {
> + template<typename _Tp, typename _Up,
> + typename _Cref = common_reference_t<const _Tp&, const _Up&>>
> + concept __comparison_common_type_with_impl
> + = same_as<common_reference_t<const _Tp&, const _Up&>,
> + common_reference_t<const _Up&, const _Tp&>>
> + && requires {
> + requires convertible_to<const _Tp&, const _Cref&>
> + || convertible_to<_Tp, const _Cref&>;
> + requires convertible_to<const _Up&, const _Cref&>
> + || convertible_to<_Up, const _Cref&>;
> + };
> +
> + template<typename _Tp, typename _Up>
> + concept __comparison_common_type_with
> + = __comparison_common_type_with_impl<remove_cvref_t<_Tp>,
> + remove_cvref_t<_Up>>;
> + } // namespace __detail
> +
> // [concept.equalitycomparable], concept equality_comparable
>
> namespace __detail
> @@ -316,7 +337,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> template<typename _Tp, typename _Up>
> concept equality_comparable_with
> = equality_comparable<_Tp> && equality_comparable<_Up>
> - && common_reference_with<__detail::__cref<_Tp>,
> __detail::__cref<_Up>>
> + && __detail::__comparison_common_type_with<_Tp, _Up>
> && equality_comparable<common_reference_t<__detail::__cref<_Tp>,
> __detail::__cref<_Up>>>
> && __detail::__weakly_eq_cmp_with<_Tp, _Up>;
> diff --git a/libstdc++-v3/libsupc++/compare
> b/libstdc++-v3/libsupc++/compare
> index 08f2b2ba47ed..5b4f47a94e3b 100644
> --- a/libstdc++-v3/libsupc++/compare
> +++ b/libstdc++-v3/libsupc++/compare
> @@ -34,6 +34,7 @@
> #pragma GCC system_header
> #endif
>
> +#define __glibcxx_want_concepts
> #define __glibcxx_want_three_way_comparison
> #define __glibcxx_want_type_order
> #include <bits/version.h>
> @@ -499,10 +500,8 @@ namespace std _GLIBCXX_VISIBILITY(default)
>
> template<typename _Tp, typename _Up, typename _Cat = partial_ordering>
> concept three_way_comparable_with
> - = three_way_comparable<_Tp, _Cat>
> - && three_way_comparable<_Up, _Cat>
> - && common_reference_with<const remove_reference_t<_Tp>&,
> - const remove_reference_t<_Up>&>
> + = three_way_comparable<_Tp, _Cat> && three_way_comparable<_Up, _Cat>
> + && __detail::__comparison_common_type_with<_Tp, _Up>
> && three_way_comparable<
> common_reference_t<const remove_reference_t<_Tp>&,
> const remove_reference_t<_Up>&>, _Cat>
> diff --git
> a/libstdc++-v3/testsuite/std/concepts/concepts.compare/move_only.cc
> b/libstdc++-v3/testsuite/std/concepts/concepts.compare/move_only.cc
> new file mode 100644
> index 000000000000..01870d846267
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/std/concepts/concepts.compare/move_only.cc
> @@ -0,0 +1,28 @@
> +// { dg-do compile { target c++20 } }
> +
> +#include <concepts>
> +#include <compare>
> +
> +// P2404R3 Move-only types for equality_comparable_with,
> +// totally_ordered_with, and three_way_comparable_with
> +
> +// This was approved for C++23 but we treat it as a DR for C++20.
> +
> +#ifndef __cpp_lib_concepts
> +# error "Feature-test macro __cpp_lib_concepts is missing in <compare>"
> +#elif __cpp_lib_concepts < 202207L
> +# error "Feature-test macro __cpp_lib_concepts has wrong value in
> <compare>"
> +#endif
> +
> +struct MoveOnly
> +{
> + MoveOnly(int);
> + MoveOnly(MoveOnly&&) = default;
> + auto operator<=>(const MoveOnly&) const = default;
> + std::strong_ordering operator<=>(int) const;
> + bool operator==(const MoveOnly&) const;
> +};
> +
> +static_assert(std::equality_comparable_with<MoveOnly, int>);
> +static_assert(std::totally_ordered_with<MoveOnly, int>);
> +static_assert(std::three_way_comparable_with<MoveOnly, int>);
> --
> 2.52.0
>
>