From f5fa62ed19a1c85cda920bbe05eb075d8f2a0b42 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Tue, 14 Apr 2020 21:54:55 +0100 Subject: [PATCH] libstdc++: Add comparison operators to std::shared_ptr (PR 94562) This also implements the proposed resolution to LWG issue 3247, so that the ill-formed <=> expression with nullptr is not used. PR libstdc++/94562 * include/bits/shared_ptr.h (operator<=>): Define for C++20. * include/bits/shared_ptr_base.h (operator<=>): Likewise. * include/bits/unique_ptr.h (operator<=>): Add inline specifier. * testsuite/20_util/shared_ptr/comparison/cmp_c++20.cc: New test. * testsuite/20_util/shared_ptr/comparison/less.cc: Do not expect std::less to be used when comparing std::shared_ptr objects in C++20. --- libstdc++-v3/ChangeLog | 9 ++ libstdc++-v3/include/bits/shared_ptr.h | 16 +++ libstdc++-v3/include/bits/shared_ptr_base.h | 19 ++++ libstdc++-v3/include/bits/unique_ptr.h | 4 +- .../shared_ptr/comparison/cmp_c++20.cc | 106 ++++++++++++++++++ .../20_util/shared_ptr/comparison/less.cc | 6 +- 6 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/shared_ptr/comparison/cmp_c++20.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 085ca6aec45..417c8c440c6 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,5 +1,14 @@ 2020-04-14 Jonathan Wakely + PR libstdc++/94562 + * include/bits/shared_ptr.h (operator<=>): Define for C++20. + * include/bits/shared_ptr_base.h (operator<=>): Likewise. + * include/bits/unique_ptr.h (operator<=>): Add inline specifier. + * testsuite/20_util/shared_ptr/comparison/cmp_c++20.cc: New test. + * testsuite/20_util/shared_ptr/comparison/less.cc: Do not expect + std::less to be used when comparing std::shared_ptr objects in + C++20. + PR libstdc++/94565 * libsupc++/compare (__unspec): Add noexcept-specifier to constructor. * testsuite/18_support/comparisons/categories/94565.cc: New test. diff --git a/libstdc++-v3/include/bits/shared_ptr.h b/libstdc++-v3/include/bits/shared_ptr.h index c4df3582e20..0c393e23132 100644 --- a/libstdc++-v3/include/bits/shared_ptr.h +++ b/libstdc++-v3/include/bits/shared_ptr.h @@ -442,6 +442,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION operator==(const shared_ptr<_Tp>& __a, nullptr_t) noexcept { return !__a; } +#ifdef __cpp_lib_three_way_comparison + template + inline strong_ordering + operator<=>(const shared_ptr<_Tp>& __a, + const shared_ptr<_Up>& __b) noexcept + { return compare_three_way()(__a.get(), __b.get()); } + + template + inline strong_ordering + operator<=>(const shared_ptr<_Tp>& __a, nullptr_t) noexcept + { + using pointer = typename shared_ptr<_Tp>::element_type*; + return compare_three_way()(__a.get(), static_cast(nullptr)); + } +#else /// shared_ptr comparison with nullptr template _GLIBCXX_NODISCARD inline bool @@ -548,6 +563,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX_NODISCARD inline bool operator>=(nullptr_t, const shared_ptr<_Tp>& __a) noexcept { return !(nullptr < __a); } +#endif // 20.7.2.2.8 shared_ptr specialized algorithms. diff --git a/libstdc++-v3/include/bits/shared_ptr_base.h b/libstdc++-v3/include/bits/shared_ptr_base.h index c9017ede4cb..ff578e66117 100644 --- a/libstdc++-v3/include/bits/shared_ptr_base.h +++ b/libstdc++-v3/include/bits/shared_ptr_base.h @@ -54,6 +54,9 @@ #include #include #include +#if __cplusplus > 201703L +# include +#endif namespace std _GLIBCXX_VISIBILITY(default) { @@ -1442,6 +1445,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION operator==(const __shared_ptr<_Tp, _Lp>& __a, nullptr_t) noexcept { return !__a; } +#ifdef __cpp_lib_three_way_comparison + template + inline strong_ordering + operator<=>(const __shared_ptr<_Tp, _Lp>& __a, + const __shared_ptr<_Up, _Lp>& __b) noexcept + { return compare_three_way()(__a.get(), __b.get()); } + + template + inline strong_ordering + operator<=>(const __shared_ptr<_Tp, _Lp>& __a, nullptr_t) noexcept + { + using pointer = typename __shared_ptr<_Tp, _Lp>::element_type*; + return compare_three_way()(__a.get(), static_cast(nullptr)); + } +#else template inline bool operator==(nullptr_t, const __shared_ptr<_Tp, _Lp>& __a) noexcept @@ -1537,6 +1555,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION inline bool operator>=(nullptr_t, const __shared_ptr<_Tp, _Lp>& __a) noexcept { return !(nullptr < __a); } +#endif // three-way comparison // 20.7.2.2.8 shared_ptr specialized algorithms. template diff --git a/libstdc++-v3/include/bits/unique_ptr.h b/libstdc++-v3/include/bits/unique_ptr.h index 53c8def627d..3695214808b 100644 --- a/libstdc++-v3/include/bits/unique_ptr.h +++ b/libstdc++-v3/include/bits/unique_ptr.h @@ -888,6 +888,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template requires three_way_comparable_with::pointer, typename unique_ptr<_Up, _Ep>::pointer> + inline compare_three_way_result_t::pointer, typename unique_ptr<_Up, _Ep>::pointer> operator<=>(const unique_ptr<_Tp, _Dp>& __x, @@ -896,11 +897,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template requires three_way_comparable::pointer> + inline compare_three_way_result_t::pointer> operator<=>(const unique_ptr<_Tp, _Dp>& __x, nullptr_t) { using pointer = typename unique_ptr<_Tp, _Dp>::pointer; - return compare_three_way()(__x.get(), pointer(nullptr)); + return compare_three_way()(__x.get(), static_cast(nullptr)); } #endif // @} relates unique_ptr diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/comparison/cmp_c++20.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/comparison/cmp_c++20.cc new file mode 100644 index 00000000000..5600bbff932 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/comparison/cmp_c++20.cc @@ -0,0 +1,106 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include +#include + +void +test01() +{ + std::shared_ptr p0, p00; + VERIFY( p0 == p00 ); + VERIFY( !(p0 < p00) ); + VERIFY( !(p0 > p00) ); + VERIFY( p0 <= p00 ); + VERIFY( p0 >= p00 ); + VERIFY( std::is_eq(p0 <=> p00) ); + + std::shared_ptr p1(new int(1)); + VERIFY( p1 == p1 ); + VERIFY( !(p1 < p1) ); + VERIFY( !(p1 > p1) ); + VERIFY( p1 <= p1 ); + VERIFY( p1 >= p1 ); + VERIFY( std::is_eq(p1 <=> p1) ); + + std::shared_ptr p11 = p1; + VERIFY( p11 == p1 ); + VERIFY( !(p11 < p1) ); + VERIFY( !(p11 > p1) ); + VERIFY( p11 <= p1 ); + VERIFY( p11 >= p1 ); + VERIFY( std::is_eq(p11 <=> p1) ); + + std::shared_ptr p2(new int(1)); + VERIFY( p1 >= p1 ); + VERIFY( p1 != p2 ); + VERIFY( (p1 < p2) || (p1 > p2) ); + VERIFY( (p1 <= p2) || (p1 >= p2) ); + VERIFY( std::is_neq(p1 <=> p2) ); + + VERIFY( p1 != p0 ); + VERIFY( !(p1 < p0) ); + VERIFY( p1 > p0 ); + VERIFY( !(p1 <= p0) ); + VERIFY( p1 >= p0 ); + VERIFY( std::is_gt(p1 <=> p0) ); + VERIFY( std::is_lt(p0 <=> p1) ); +} + +void +test02() +{ + std::shared_ptr p0; + VERIFY( p0 == nullptr ); + VERIFY( !(p0 < nullptr) ); + VERIFY( !(p0 > nullptr) ); + VERIFY( p0 <= nullptr ); + VERIFY( p0 >= nullptr ); + VERIFY( std::is_eq(p0 <=> nullptr) ); + + VERIFY( nullptr == p0 ); + VERIFY( !(nullptr < p0) ); + VERIFY( !(nullptr > p0) ); + VERIFY( nullptr <= p0 ); + VERIFY( nullptr >= p0 ); + VERIFY( std::is_eq(nullptr <=> p0) ); + + std::shared_ptr p1(new int(1)); + VERIFY( p1 != nullptr ); + VERIFY( !(p1 < nullptr) ); + VERIFY( p1 > nullptr ); + VERIFY( !(p1 <= nullptr) ); + VERIFY( p1 >= nullptr ); + VERIFY( std::is_gt(p1 <=> nullptr) ); + + VERIFY( nullptr != p1 ); + VERIFY( nullptr < p1 ); + VERIFY( !(nullptr > p1) ); + VERIFY( nullptr <= p1 ); + VERIFY( !(nullptr >= p1) ); + VERIFY( std::is_lt(nullptr <=> p1) ); +} + +int +main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/comparison/less.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/comparison/less.cc index 8b42b3a9c15..9ee65ffdf09 100644 --- a/libstdc++-v3/testsuite/20_util/shared_ptr/comparison/less.cc +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/comparison/less.cc @@ -46,7 +46,11 @@ test01() std::shared_ptr p1; std::shared_ptr p2; VERIFY( !less(p1, p2) && !less(p2, p1) ); +#ifndef __cpp_lib_three_way_comparison +// In C++20 std::less> uses the operator< synthesized +// from operator<=>, which uses std::compare_three_way not std::less. VERIFY( std::less::count == 2 ); +#endif return 0; } @@ -86,7 +90,7 @@ test03() return 0; } -int +int main() { test01(); -- 2.30.2