Skip to content

Commit 8afa6cf

Browse files
authored
[libc++][functional] P2944R3 (partial): Comparisons for reference_wrapper (reference_wrapper operators only) (#88384)
Implements https://wg21.link/P2944R3 (partially) Implements https://wg21.link/LWG4071 / https://cplusplus.github.io/LWG/issue4071 (fixes build failures in the test suite) - https://eel.is/c++draft/refwrap.comparisons
1 parent 105dd60 commit 8afa6cf

19 files changed

+614
-26
lines changed

libcxx/docs/FeatureTestMacroTable.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ Status
446446
---------------------------------------------------------- -----------------
447447
``__cpp_lib_rcu`` *unimplemented*
448448
---------------------------------------------------------- -----------------
449-
``__cpp_lib_reference_wrapper`` *unimplemented*
449+
``__cpp_lib_reference_wrapper`` ``202403L``
450450
---------------------------------------------------------- -----------------
451451
``__cpp_lib_saturation_arithmetic`` ``202311L``
452452
---------------------------------------------------------- -----------------

libcxx/docs/ReleaseNotes/19.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ Implemented Papers
4646
- P2869R4 - Remove Deprecated ``shared_ptr`` Atomic Access APIs from C++26
4747
- P2872R3 - Remove ``wstring_convert`` From C++26
4848
- P3142R0 - Printing Blank Lines with ``println`` (as DR against C++23)
49+
- P2944R3 - Comparisons for ``reference_wrapper`` (comparison operators for ``reference_wrapper`` only)
4950
- P2302R4 - ``std::ranges::contains``
5051
- P1659R3 - ``std::ranges::starts_with`` and ``std::ranges::ends_with``
5152
- P3029R1 - Better ``mdspan``'s CTAD

libcxx/docs/Status/Cxx2c.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Paper Status
4141

4242
.. [#note-P2510R3] This paper is applied as DR against C++20. (MSVC STL and libstdc++ will do the same.)
4343
.. [#note-P3142R0] This paper is applied as DR against C++23. (MSVC STL and libstdc++ will do the same.)
44+
.. [#note-P2944R3] Implemented comparisons for ``reference_wrapper`` only.
4445
4546
.. _issues-status-cxx2c:
4647

libcxx/docs/Status/Cxx2cIssues.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,5 @@
6464
"","","","","",""
6565
"`3343 <https://wg21.link/LWG3343>`__","Ordering of calls to ``unlock()`` and ``notify_all()`` in Effects element of ``notify_all_at_thread_exit()`` should be reversed","Not Yet Adopted","|Complete|","16.0",""
6666
"XXXX","","The sys_info range should be affected by save","Not Yet Adopted","|Complete|","19.0"
67+
"`4071 <https://wg21.link/LWG4071>`__","","``reference_wrapper`` comparisons are not SFINAE-friendly","Not Yet Adopted","|Complete|","19.0"
6768
"","","","","",""

libcxx/docs/Status/Cxx2cPapers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
"`P2248R8 <https://wg21.link/P2248R8>`__","LWG","Enabling list-initialization for algorithms","Tokyo March 2024","","",""
6060
"`P2810R4 <https://wg21.link/P2810R4>`__","LWG","``is_debugger_present`` ``is_replaceable``","Tokyo March 2024","","",""
6161
"`P1068R11 <https://wg21.link/P1068R11>`__","LWG","Vector API for random number generation","Tokyo March 2024","","",""
62-
"`P2944R3 <https://wg21.link/P2944R3>`__","LWG","Comparisons for ``reference_wrapper``","Tokyo March 2024","","",""
62+
"`P2944R3 <https://wg21.link/P2944R3>`__","LWG","Comparisons for ``reference_wrapper``","Tokyo March 2024","|Partial| [#note-P2944R3]_","19.0",""
6363
"`P2642R6 <https://wg21.link/P2642R6>`__","LWG","Padded ``mdspan`` layouts","Tokyo March 2024","","",""
6464
"`P3029R1 <https://wg21.link/P3029R1>`__","LWG","Better ``mdspan``'s CTAD","Tokyo March 2024","|Complete|","19.0",""
6565
"","","","","","",""

libcxx/include/__functional/reference_wrapper.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@
1010
#ifndef _LIBCPP___FUNCTIONAL_REFERENCE_WRAPPER_H
1111
#define _LIBCPP___FUNCTIONAL_REFERENCE_WRAPPER_H
1212

13+
#include <__compare/synth_three_way.h>
14+
#include <__concepts/boolean_testable.h>
1315
#include <__config>
1416
#include <__functional/invoke.h>
1517
#include <__functional/weak_result_type.h>
1618
#include <__memory/addressof.h>
1719
#include <__type_traits/enable_if.h>
20+
#include <__type_traits/is_const.h>
1821
#include <__type_traits/remove_cvref.h>
1922
#include <__type_traits/void_t.h>
2023
#include <__utility/declval.h>
@@ -64,6 +67,54 @@ class _LIBCPP_TEMPLATE_VIS reference_wrapper : public __weak_result_type<_Tp> {
6467
{
6568
return std::__invoke(get(), std::forward<_ArgTypes>(__args)...);
6669
}
70+
71+
#if _LIBCPP_STD_VER >= 26
72+
73+
// [refwrap.comparisons], comparisons
74+
75+
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(reference_wrapper __x, reference_wrapper __y)
76+
requires requires {
77+
{ __x.get() == __y.get() } -> __boolean_testable;
78+
}
79+
{
80+
return __x.get() == __y.get();
81+
}
82+
83+
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(reference_wrapper __x, const _Tp& __y)
84+
requires requires {
85+
{ __x.get() == __y } -> __boolean_testable;
86+
}
87+
{
88+
return __x.get() == __y;
89+
}
90+
91+
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(reference_wrapper __x, reference_wrapper<const _Tp> __y)
92+
requires(!is_const_v<_Tp>) && requires {
93+
{ __x.get() == __y.get() } -> __boolean_testable;
94+
}
95+
{
96+
return __x.get() == __y.get();
97+
}
98+
99+
_LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(reference_wrapper __x, reference_wrapper __y)
100+
requires requires { std::__synth_three_way(__x.get(), __y.get()); }
101+
{
102+
return std::__synth_three_way(__x.get(), __y.get());
103+
}
104+
105+
_LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(reference_wrapper __x, const _Tp& __y)
106+
requires requires { std::__synth_three_way(__x.get(), __y); }
107+
{
108+
return std::__synth_three_way(__x.get(), __y);
109+
}
110+
111+
_LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(reference_wrapper __x, reference_wrapper<const _Tp> __y)
112+
requires(!is_const_v<_Tp>) && requires { std::__synth_three_way(__x.get(), __y.get()); }
113+
{
114+
return std::__synth_three_way(__x.get(), __y.get());
115+
}
116+
117+
#endif // _LIBCPP_STD_VER >= 26
67118
};
68119

69120
#if _LIBCPP_STD_VER >= 17

libcxx/include/functional

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,15 @@ template <class T> struct unwrap_ref_decay : unwrap_reference<decay_t<T>> { };
7777
template <class T> using unwrap_reference_t = typename unwrap_reference<T>::type; // since C++20
7878
template <class T> using unwrap_ref_decay_t = typename unwrap_ref_decay<T>::type; // since C++20
7979
80+
// [refwrap.comparisons], comparisons
81+
friend constexpr bool operator==(reference_wrapper, reference_wrapper); // Since C++26
82+
friend constexpr bool operator==(reference_wrapper, const T&); // Since C++26
83+
friend constexpr bool operator==(reference_wrapper, reference_wrapper<const T>); // Since C++26
84+
85+
friend constexpr auto operator<=>(reference_wrapper, reference_wrapper); // Since C++26
86+
friend constexpr auto operator<=>(reference_wrapper, const T&); // Since C++26
87+
friend constexpr auto operator<=>(reference_wrapper, reference_wrapper<const T>); // Since C++26
88+
8089
template <class T> // <class T=void> in C++14
8190
struct plus {
8291
T operator()(const T& x, const T& y) const;

libcxx/include/version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
526526
// # define __cpp_lib_ranges_concat 202403L
527527
# define __cpp_lib_ratio 202306L
528528
// # define __cpp_lib_rcu 202306L
529-
// # define __cpp_lib_reference_wrapper 202403L
529+
# define __cpp_lib_reference_wrapper 202403L
530530
# define __cpp_lib_saturation_arithmetic 202311L
531531
// # define __cpp_lib_smart_ptr_owner_equality 202306L
532532
# define __cpp_lib_span_at 202311L

libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -535,17 +535,11 @@
535535
# error "__cpp_lib_ranges should have the value 202207L in c++26"
536536
# endif
537537

538-
# if !defined(_LIBCPP_VERSION)
539-
# ifndef __cpp_lib_reference_wrapper
540-
# error "__cpp_lib_reference_wrapper should be defined in c++26"
541-
# endif
542-
# if __cpp_lib_reference_wrapper != 202403L
543-
# error "__cpp_lib_reference_wrapper should have the value 202403L in c++26"
544-
# endif
545-
# else // _LIBCPP_VERSION
546-
# ifdef __cpp_lib_reference_wrapper
547-
# error "__cpp_lib_reference_wrapper should not be defined because it is unimplemented in libc++!"
548-
# endif
538+
# ifndef __cpp_lib_reference_wrapper
539+
# error "__cpp_lib_reference_wrapper should be defined in c++26"
540+
# endif
541+
# if __cpp_lib_reference_wrapper != 202403L
542+
# error "__cpp_lib_reference_wrapper should have the value 202403L in c++26"
549543
# endif
550544

551545
# ifndef __cpp_lib_result_of_sfinae

libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7464,17 +7464,11 @@
74647464
# endif
74657465
# endif
74667466

7467-
# if !defined(_LIBCPP_VERSION)
7468-
# ifndef __cpp_lib_reference_wrapper
7469-
# error "__cpp_lib_reference_wrapper should be defined in c++26"
7470-
# endif
7471-
# if __cpp_lib_reference_wrapper != 202403L
7472-
# error "__cpp_lib_reference_wrapper should have the value 202403L in c++26"
7473-
# endif
7474-
# else // _LIBCPP_VERSION
7475-
# ifdef __cpp_lib_reference_wrapper
7476-
# error "__cpp_lib_reference_wrapper should not be defined because it is unimplemented in libc++!"
7477-
# endif
7467+
# ifndef __cpp_lib_reference_wrapper
7468+
# error "__cpp_lib_reference_wrapper should be defined in c++26"
7469+
# endif
7470+
# if __cpp_lib_reference_wrapper != 202403L
7471+
# error "__cpp_lib_reference_wrapper should have the value 202403L in c++26"
74787472
# endif
74797473

74807474
# ifndef __cpp_lib_remove_cvref
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
10+
11+
// <functional>
12+
13+
// class reference_wrapper
14+
15+
// [refwrap.comparisons], comparisons
16+
17+
// friend constexpr auto operator<=>(reference_wrapper, const T&); // Since C++26
18+
19+
#include <cassert>
20+
#include <concepts>
21+
#include <functional>
22+
23+
#include "test_comparisons.h"
24+
#include "test_macros.h"
25+
26+
#include "helper_concepts.h"
27+
#include "helper_types.h"
28+
29+
// Test SFINAE.
30+
31+
static_assert(HasSpaceshipOperatorWithInt<std::reference_wrapper<StrongOrder>>);
32+
static_assert(HasSpaceshipOperatorWithInt<std::reference_wrapper<WeakOrder>>);
33+
static_assert(HasSpaceshipOperatorWithInt<std::reference_wrapper<PartialOrder>>);
34+
35+
static_assert(!HasSpaceshipOperatorWithInt<std::reference_wrapper<NonComparable>>);
36+
37+
// Test comparisons.
38+
39+
template <typename T, typename Order>
40+
constexpr void test() {
41+
T t{47};
42+
43+
T bigger{94};
44+
T smaller{82};
45+
46+
T unordered{std::numeric_limits<int>::min()};
47+
48+
// Identical contents
49+
{
50+
std::reference_wrapper<T> rw1{t};
51+
assert(testOrder(rw1, t, Order::equivalent));
52+
}
53+
// Less
54+
{
55+
std::reference_wrapper<T> rw1{smaller};
56+
assert(testOrder(rw1, bigger, Order::less));
57+
}
58+
// Greater
59+
{
60+
std::reference_wrapper<T> rw1{bigger};
61+
assert(testOrder(rw1, smaller, Order::greater));
62+
}
63+
// Unordered
64+
if constexpr (std::same_as<T, PartialOrder>) {
65+
std::reference_wrapper<T> rw1{bigger};
66+
assert(testOrder(rw1, unordered, Order::unordered));
67+
}
68+
}
69+
70+
constexpr bool test() {
71+
test<int, std::strong_ordering>();
72+
test<StrongOrder, std::strong_ordering>();
73+
test<int, std::weak_ordering>();
74+
test<WeakOrder, std::weak_ordering>();
75+
test<int, std::partial_ordering>();
76+
test<PartialOrder, std::partial_ordering>();
77+
78+
// `LessAndEqComp` does not have `operator<=>`. Ordering is synthesized based on `operator<`
79+
test<LessAndEqComp, std::weak_ordering>();
80+
81+
return true;
82+
}
83+
84+
int main(int, char**) {
85+
test();
86+
static_assert(test());
87+
88+
return 0;
89+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
10+
11+
// <functional>
12+
13+
// class reference_wrapper
14+
15+
// [refwrap.comparisons], comparisons
16+
17+
// friend constexpr auto operator<=>(reference_wrapper, reference_wrapper); // Since C++26
18+
19+
#include <cassert>
20+
#include <concepts>
21+
#include <functional>
22+
23+
#include "test_comparisons.h"
24+
#include "test_macros.h"
25+
26+
#include "helper_concepts.h"
27+
#include "helper_types.h"
28+
29+
// Test SFINAE.
30+
31+
static_assert(std::three_way_comparable<std::reference_wrapper<StrongOrder>>);
32+
static_assert(std::three_way_comparable<std::reference_wrapper<WeakOrder>>);
33+
static_assert(std::three_way_comparable<std::reference_wrapper<PartialOrder>>);
34+
35+
static_assert(!std::three_way_comparable<std::reference_wrapper<NonComparable>>);
36+
37+
// Test comparisons.
38+
39+
template <typename T, typename Order>
40+
constexpr void test() {
41+
T t{47};
42+
43+
T bigger{94};
44+
T smaller{82};
45+
46+
T unordered{std::numeric_limits<int>::min()};
47+
48+
// Identical contents
49+
{
50+
std::reference_wrapper<T> rw1{t};
51+
std::reference_wrapper<T> rw2{t};
52+
assert(testOrder(rw1, rw2, Order::equivalent));
53+
}
54+
// Less
55+
{
56+
std::reference_wrapper<T> rw1{smaller};
57+
std::reference_wrapper<T> rw2{bigger};
58+
assert(testOrder(rw1, rw2, Order::less));
59+
}
60+
// Greater
61+
{
62+
std::reference_wrapper<T> rw1{bigger};
63+
std::reference_wrapper<T> rw2{smaller};
64+
assert(testOrder(rw1, rw2, Order::greater));
65+
}
66+
// Unordered
67+
if constexpr (std::same_as<T, PartialOrder>) {
68+
std::reference_wrapper<T> rw1{bigger};
69+
std::reference_wrapper<T> rw2{unordered};
70+
assert(testOrder(rw1, rw2, Order::unordered));
71+
}
72+
}
73+
74+
constexpr bool test() {
75+
test<int, std::strong_ordering>();
76+
test<StrongOrder, std::strong_ordering>();
77+
test<int, std::weak_ordering>();
78+
test<WeakOrder, std::weak_ordering>();
79+
test<int, std::partial_ordering>();
80+
test<PartialOrder, std::partial_ordering>();
81+
82+
// `LessAndEqComp` does not have `operator<=>`. Ordering is synthesized based on `operator<`
83+
test<LessAndEqComp, std::weak_ordering>();
84+
85+
return true;
86+
}
87+
88+
int main(int, char**) {
89+
test();
90+
static_assert(test());
91+
92+
return 0;
93+
}

0 commit comments

Comments
 (0)