From 86c0ec1de10050af15afa2965adf6d219f0ccd06 Mon Sep 17 00:00:00 2001 From: Ville Voutilainen Date: Wed, 13 Jul 2016 14:25:51 +0300 Subject: [PATCH] Implement P0307R2, Making Optional Greater Equal Again. * include/experimental/optional (_Has_addressof): Fix the comment. * include/std/optional (_Has_addressof): Likewise. (operator=(_Up&&)): Constrain. (operator=(const optional<_Up>&)): Likewise. (operator=(optional<_Up>&&)): Likewise. (__optional_relop_t): New. (operator==(const optional<_Tp>&, const optional<_Tp>&)): Constrain. (operator!=(const optional<_Tp>&, const optional<_Tp>&)): Constrain and make transparent. (operator<(const optional<_Tp>&, const optional<_Tp>&)): Constrain. (operator>(const optional<_Tp>&, const optional<_Tp>&)): Constrain and make transparent. (operator<=(const optional<_Tp>&, const optional<_Tp>&)): Likewise. (operator>=(const optional<_Tp>&, const optional<_Tp>&)): Likewise. (operator==(const optional<_Tp>&, const _Tp&): Constrain. (operator==(const _Tp&, const optional<_Tp>&)): Likewise. (operator!=(const optional<_Tp>&, _Tp const&)): Constrain and make transparent. (operator!=(const _Tp&, const optional<_Tp>&)): Likewise. (operator<(const optional<_Tp>&, const _Tp&)): Constrain. (operator<(const _Tp&, const optional<_Tp>&)): Likewise. (operator>(const optional<_Tp>&, const _Tp&)): Constrain and make transparent. (operator>(const _Tp&, const optional<_Tp>&)): Likewise. (operator<=(const optional<_Tp>&, const _Tp&)): Likewise. (operator<=(const _Tp&, const optional<_Tp>&)): Likewise. (operator>=(const optional<_Tp>&, const _Tp&)): Likewise. (operator>=(const _Tp&, const optional<_Tp>&)): Likewise. * testsuite/20_util/optional/constexpr/relops/2.cc: Adjust. * testsuite/20_util/optional/constexpr/relops/4.cc: Likewise. * testsuite/20_util/optional/relops/1.cc: Likewise. * testsuite/20_util/optional/relops/2.cc: Likewise. * testsuite/20_util/optional/relops/3.cc: Likewise. * testsuite/20_util/optional/relops/4.cc: Likewise. * testsuite/20_util/optional/requirements.cc: Add tests to verify that optional's relops are transparent and don't synthesize operators. Also test that assignment sfinaes. From-SVN: r238292 --- libstdc++-v3/ChangeLog | 39 ++++++ libstdc++-v3/include/experimental/optional | 4 +- libstdc++-v3/include/std/optional | 125 +++++++++++------- .../20_util/optional/constexpr/relops/2.cc | 12 ++ .../20_util/optional/constexpr/relops/4.cc | 12 ++ .../testsuite/20_util/optional/relops/1.cc | 16 +++ .../testsuite/20_util/optional/relops/2.cc | 16 +++ .../testsuite/20_util/optional/relops/3.cc | 16 +++ .../testsuite/20_util/optional/relops/4.cc | 16 +++ .../20_util/optional/requirements.cc | 79 +++++++++++ 10 files changed, 283 insertions(+), 52 deletions(-) diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 17e612b2659..f4d09963b57 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,42 @@ + Implement P0307R2, Making Optional Greater Equal Again. + * include/experimental/optional (_Has_addressof): Fix the comment. + * include/std/optional (_Has_addressof): Likewise. + (operator=(_Up&&)): Constrain. + (operator=(const optional<_Up>&)): Likewise. + (operator=(optional<_Up>&&)): Likewise. + (__optional_relop_t): New. + (operator==(const optional<_Tp>&, const optional<_Tp>&)): Constrain. + (operator!=(const optional<_Tp>&, const optional<_Tp>&)): + Constrain and make transparent. + (operator<(const optional<_Tp>&, const optional<_Tp>&)): Constrain. + (operator>(const optional<_Tp>&, const optional<_Tp>&)): + Constrain and make transparent. + (operator<=(const optional<_Tp>&, const optional<_Tp>&)): Likewise. + (operator>=(const optional<_Tp>&, const optional<_Tp>&)): Likewise. + (operator==(const optional<_Tp>&, const _Tp&): Constrain. + (operator==(const _Tp&, const optional<_Tp>&)): Likewise. + (operator!=(const optional<_Tp>&, _Tp const&)): + Constrain and make transparent. + (operator!=(const _Tp&, const optional<_Tp>&)): Likewise. + (operator<(const optional<_Tp>&, const _Tp&)): Constrain. + (operator<(const _Tp&, const optional<_Tp>&)): Likewise. + (operator>(const optional<_Tp>&, const _Tp&)): + Constrain and make transparent. + (operator>(const _Tp&, const optional<_Tp>&)): Likewise. + (operator<=(const optional<_Tp>&, const _Tp&)): Likewise. + (operator<=(const _Tp&, const optional<_Tp>&)): Likewise. + (operator>=(const optional<_Tp>&, const _Tp&)): Likewise. + (operator>=(const _Tp&, const optional<_Tp>&)): Likewise. + * testsuite/20_util/optional/constexpr/relops/2.cc: Adjust. + * testsuite/20_util/optional/constexpr/relops/4.cc: Likewise. + * testsuite/20_util/optional/relops/1.cc: Likewise. + * testsuite/20_util/optional/relops/2.cc: Likewise. + * testsuite/20_util/optional/relops/3.cc: Likewise. + * testsuite/20_util/optional/relops/4.cc: Likewise. + * testsuite/20_util/optional/requirements.cc: Add tests to verify + that optional's relops are transparent and don't synthesize + operators. Also test that assignment sfinaes. + 2016-07-13 Jonathan Wakely * include/bits/basic_string.h [_GLIBCXX_USE_CXX11_ABI] (_M_c_str): diff --git a/libstdc++-v3/include/experimental/optional b/libstdc++-v3/include/experimental/optional index b6425b7d00e..ea8f6fb1c64 100644 --- a/libstdc++-v3/include/experimental/optional +++ b/libstdc++-v3/include/experimental/optional @@ -155,8 +155,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @brief Trait that detects the presence of an overloaded unary operator&. * * Practically speaking this detects the presence of such an operator when - * called on a const-qualified lvalue (i.e. - * declval<_Tp * const&>().operator&()). + * called on a const-qualified lvalue (e.g. + * declval().operator&()). */ template struct _Has_addressof diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional index e9a86a44aa2..f1bb17c75dc 100644 --- a/libstdc++-v3/include/std/optional +++ b/libstdc++-v3/include/std/optional @@ -131,8 +131,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @brief Trait that detects the presence of an overloaded unary operator&. * * Practically speaking this detects the presence of such an operator when - * called on a const-qualified lvalue (i.e. - * declval<_Tp * const&>().operator&()). + * called on a const-qualified lvalue (e.g. + * declval().operator&()). */ template struct _Has_addressof @@ -577,16 +577,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template, + is_assignable<_Tp&, _Up>, __not_>, __not_<__is_optional<_Up>>>::value, bool> = true> optional& operator=(_Up&& __u) { - static_assert(__and_, - is_assignable<_Tp&, _Up>>(), - "Cannot assign to value type from argument"); - if (this->_M_is_engaged()) this->_M_get() = std::forward<_Up>(__u); else @@ -597,15 +595,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template>>::value, - bool> = true> + is_constructible<_Tp, _Up>, + is_assignable<_Tp&, _Up>, + __not_>>::value, + bool> = true> optional& operator=(const optional<_Up>& __u) { - static_assert(__and_, - is_assignable<_Tp&, _Up>>(), - "Cannot assign to value type from argument"); - if (__u) { if (this->_M_is_engaged()) @@ -621,16 +617,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template>>::value, - bool> = true> + enable_if_t<__and_< + is_constructible<_Tp, _Up>, + is_assignable<_Tp&, _Up>, + __not_>>::value, + bool> = true> optional& operator=(optional<_Up>&& __u) { - static_assert(__and_, - is_assignable<_Tp&, _Up>>(), - "Cannot assign to value type from argument"); - if (__u) { if (this->_M_is_engaged()) @@ -785,41 +779,60 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } }; + template + using __optional_relop_t = + enable_if_t::value, bool>; + // [X.Y.8] Comparisons between optional values. template - constexpr bool + constexpr auto operator==(const optional<_Tp>& __lhs, const optional<_Tp>& __rhs) + -> __optional_relop_t() == declval<_Tp>())> { return static_cast(__lhs) == static_cast(__rhs) && (!__lhs || *__lhs == *__rhs); } template - constexpr bool + constexpr auto operator!=(const optional<_Tp>& __lhs, const optional<_Tp>& __rhs) - { return !(__lhs == __rhs); } + -> __optional_relop_t() != declval<_Tp>())> + { + return static_cast(__lhs) != static_cast(__rhs) + || (static_cast(__lhs) && *__lhs != *__rhs); + } template - constexpr bool + constexpr auto operator<(const optional<_Tp>& __lhs, const optional<_Tp>& __rhs) + -> __optional_relop_t() < declval<_Tp>())> { return static_cast(__rhs) && (!__lhs || *__lhs < *__rhs); } template - constexpr bool + constexpr auto operator>(const optional<_Tp>& __lhs, const optional<_Tp>& __rhs) - { return __rhs < __lhs; } + -> __optional_relop_t() > declval<_Tp>())> + { + return static_cast(__lhs) && (!__rhs || *__lhs > *__rhs); + } template - constexpr bool + constexpr auto operator<=(const optional<_Tp>& __lhs, const optional<_Tp>& __rhs) - { return !(__rhs < __lhs); } + -> __optional_relop_t() <= declval<_Tp>())> + { + return !__lhs || (static_cast(__rhs) && *__lhs <= *__rhs); + } template - constexpr bool + constexpr auto operator>=(const optional<_Tp>& __lhs, const optional<_Tp>& __rhs) - { return !(__lhs < __rhs); } + -> __optional_relop_t() >= declval<_Tp>())> + { + return !__rhs || (static_cast(__lhs) && *__lhs >= *__rhs); + } // [X.Y.9] Comparisons with nullopt. template @@ -884,64 +897,76 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // [X.Y.10] Comparisons with value type. template - constexpr bool + constexpr auto operator==(const optional<_Tp>& __lhs, const _Tp& __rhs) + -> __optional_relop_t() == declval<_Tp>())> { return __lhs && *__lhs == __rhs; } template - constexpr bool + constexpr auto operator==(const _Tp& __lhs, const optional<_Tp>& __rhs) + -> __optional_relop_t() == declval<_Tp>())> { return __rhs && __lhs == *__rhs; } template - constexpr bool - operator!=(const optional<_Tp>& __lhs, _Tp const& __rhs) - { return !__lhs || !(*__lhs == __rhs); } + constexpr auto + operator!=(const optional<_Tp>& __lhs, const _Tp& __rhs) + -> __optional_relop_t() != declval<_Tp>())> + { return !__lhs || *__lhs != __rhs; } template - constexpr bool + constexpr auto operator!=(const _Tp& __lhs, const optional<_Tp>& __rhs) - { return !__rhs || !(__lhs == *__rhs); } + -> __optional_relop_t() != declval<_Tp>())> + { return !__rhs || __lhs != *__rhs; } template - constexpr bool + constexpr auto operator<(const optional<_Tp>& __lhs, const _Tp& __rhs) + -> __optional_relop_t() < declval<_Tp>())> { return !__lhs || *__lhs < __rhs; } template - constexpr bool + constexpr auto operator<(const _Tp& __lhs, const optional<_Tp>& __rhs) + -> __optional_relop_t() < declval<_Tp>())> { return __rhs && __lhs < *__rhs; } template - constexpr bool + constexpr auto operator>(const optional<_Tp>& __lhs, const _Tp& __rhs) - { return __lhs && __rhs < *__lhs; } + -> __optional_relop_t() > declval<_Tp>())> + { return __lhs && *__lhs > __rhs; } template - constexpr bool + constexpr auto operator>(const _Tp& __lhs, const optional<_Tp>& __rhs) - { return !__rhs || *__rhs < __lhs; } + -> __optional_relop_t() > declval<_Tp>())> + { return !__rhs || __lhs > *__rhs; } template - constexpr bool + constexpr auto operator<=(const optional<_Tp>& __lhs, const _Tp& __rhs) - { return !__lhs || !(__rhs < *__lhs); } + -> __optional_relop_t() <= declval<_Tp>())> + { return !__lhs || *__lhs <= __rhs; } template - constexpr bool + constexpr auto operator<=(const _Tp& __lhs, const optional<_Tp>& __rhs) - { return __rhs && !(*__rhs < __lhs); } + -> __optional_relop_t() <= declval<_Tp>())> + { return __rhs && __lhs <= *__rhs; } template - constexpr bool + constexpr auto operator>=(const optional<_Tp>& __lhs, const _Tp& __rhs) - { return __lhs && !(*__lhs < __rhs); } + -> __optional_relop_t() >= declval<_Tp>())> + { return __lhs && *__lhs >= __rhs; } template - constexpr bool + constexpr auto operator>=(const _Tp& __lhs, const optional<_Tp>& __rhs) - { return !__rhs || !(__lhs < *__rhs); } + -> __optional_relop_t() >= declval<_Tp>())> + { return !__rhs || __lhs >= *__rhs; } // [X.Y.11] template diff --git a/libstdc++-v3/testsuite/20_util/optional/constexpr/relops/2.cc b/libstdc++-v3/testsuite/20_util/optional/constexpr/relops/2.cc index 9aa9273255b..0ce00c1052f 100644 --- a/libstdc++-v3/testsuite/20_util/optional/constexpr/relops/2.cc +++ b/libstdc++-v3/testsuite/20_util/optional/constexpr/relops/2.cc @@ -54,6 +54,18 @@ namespace ns operator<(value_type const& lhs, value_type const& rhs) { return (lhs.i < rhs.i) || (!(rhs.i < lhs.i) && strrel(lhs.s, rhs.s)); } + constexpr bool + operator>(value_type const& lhs, value_type const& rhs) + { return rhs < lhs; } + + constexpr bool + operator<=(value_type const& lhs, value_type const& rhs) + { return lhs < rhs || lhs == rhs; } + + constexpr bool + operator>=(value_type const& lhs, value_type const& rhs) + { return lhs > rhs || lhs == rhs; } + } // namespace ns int main() diff --git a/libstdc++-v3/testsuite/20_util/optional/constexpr/relops/4.cc b/libstdc++-v3/testsuite/20_util/optional/constexpr/relops/4.cc index 15130d4efbd..d6294ad9d7c 100644 --- a/libstdc++-v3/testsuite/20_util/optional/constexpr/relops/4.cc +++ b/libstdc++-v3/testsuite/20_util/optional/constexpr/relops/4.cc @@ -54,6 +54,18 @@ namespace ns operator<(value_type const& lhs, value_type const& rhs) { return (lhs.i < rhs.i) || (!(rhs.i < lhs.i) && strrel(lhs.s, rhs.s)); } + constexpr bool + operator>(value_type const& lhs, value_type const& rhs) + { return rhs < lhs; } + + constexpr bool + operator<=(value_type const& lhs, value_type const& rhs) + { return lhs < rhs || lhs == rhs; } + + constexpr bool + operator>=(value_type const& lhs, value_type const& rhs) + { return lhs > rhs || lhs == rhs; } + } // namespace ns int main() diff --git a/libstdc++-v3/testsuite/20_util/optional/relops/1.cc b/libstdc++-v3/testsuite/20_util/optional/relops/1.cc index 62770329eff..1315902239e 100644 --- a/libstdc++-v3/testsuite/20_util/optional/relops/1.cc +++ b/libstdc++-v3/testsuite/20_util/optional/relops/1.cc @@ -36,10 +36,26 @@ namespace ns operator==(value_type const& lhs, value_type const& rhs) { return std::tie(lhs.i, lhs.s) == std::tie(rhs.i, rhs.s); } + bool + operator!=(value_type const& lhs, value_type const& rhs) + { return std::tie(lhs.i, lhs.s) != std::tie(rhs.i, rhs.s); } + bool operator<(value_type const& lhs, value_type const& rhs) { return std::tie(lhs.i, lhs.s) < std::tie(rhs.i, rhs.s); } + bool + operator>(value_type const& lhs, value_type const& rhs) + { return std::tie(lhs.i, lhs.s) > std::tie(rhs.i, rhs.s); } + + bool + operator<=(value_type const& lhs, value_type const& rhs) + { return std::tie(lhs.i, lhs.s) <= std::tie(rhs.i, rhs.s); } + + bool + operator>=(value_type const& lhs, value_type const& rhs) + { return std::tie(lhs.i, lhs.s) >= std::tie(rhs.i, rhs.s); } + } // namespace ns int main() diff --git a/libstdc++-v3/testsuite/20_util/optional/relops/2.cc b/libstdc++-v3/testsuite/20_util/optional/relops/2.cc index 65071c04bce..13512657672 100644 --- a/libstdc++-v3/testsuite/20_util/optional/relops/2.cc +++ b/libstdc++-v3/testsuite/20_util/optional/relops/2.cc @@ -36,10 +36,26 @@ namespace ns operator==(value_type const& lhs, value_type const& rhs) { return std::tie(lhs.i, lhs.s) == std::tie(rhs.i, rhs.s); } + bool + operator!=(value_type const& lhs, value_type const& rhs) + { return std::tie(lhs.i, lhs.s) != std::tie(rhs.i, rhs.s); } + bool operator<(value_type const& lhs, value_type const& rhs) { return std::tie(lhs.i, lhs.s) < std::tie(rhs.i, rhs.s); } + bool + operator>(value_type const& lhs, value_type const& rhs) + { return std::tie(lhs.i, lhs.s) > std::tie(rhs.i, rhs.s); } + + bool + operator<=(value_type const& lhs, value_type const& rhs) + { return std::tie(lhs.i, lhs.s) <= std::tie(rhs.i, rhs.s); } + + bool + operator>=(value_type const& lhs, value_type const& rhs) + { return std::tie(lhs.i, lhs.s) >= std::tie(rhs.i, rhs.s); } + } // namespace ns int main() diff --git a/libstdc++-v3/testsuite/20_util/optional/relops/3.cc b/libstdc++-v3/testsuite/20_util/optional/relops/3.cc index 2fd9e8bc1e7..95fde3b4797 100644 --- a/libstdc++-v3/testsuite/20_util/optional/relops/3.cc +++ b/libstdc++-v3/testsuite/20_util/optional/relops/3.cc @@ -36,10 +36,26 @@ namespace ns operator==(value_type const& lhs, value_type const& rhs) { return std::tie(lhs.i, lhs.s) == std::tie(rhs.i, rhs.s); } + bool + operator!=(value_type const& lhs, value_type const& rhs) + { return std::tie(lhs.i, lhs.s) != std::tie(rhs.i, rhs.s); } + bool operator<(value_type const& lhs, value_type const& rhs) { return std::tie(lhs.i, lhs.s) < std::tie(rhs.i, rhs.s); } + bool + operator>(value_type const& lhs, value_type const& rhs) + { return std::tie(lhs.i, lhs.s) > std::tie(rhs.i, rhs.s); } + + bool + operator<=(value_type const& lhs, value_type const& rhs) + { return std::tie(lhs.i, lhs.s) <= std::tie(rhs.i, rhs.s); } + + bool + operator>=(value_type const& lhs, value_type const& rhs) + { return std::tie(lhs.i, lhs.s) >= std::tie(rhs.i, rhs.s); } + } // namespace ns int main() diff --git a/libstdc++-v3/testsuite/20_util/optional/relops/4.cc b/libstdc++-v3/testsuite/20_util/optional/relops/4.cc index 363e633a40c..78d0eb8b2d0 100644 --- a/libstdc++-v3/testsuite/20_util/optional/relops/4.cc +++ b/libstdc++-v3/testsuite/20_util/optional/relops/4.cc @@ -36,10 +36,26 @@ namespace ns operator==(value_type const& lhs, value_type const& rhs) { return std::tie(lhs.i, lhs.s) == std::tie(rhs.i, rhs.s); } + bool + operator!=(value_type const& lhs, value_type const& rhs) + { return std::tie(lhs.i, lhs.s) != std::tie(rhs.i, rhs.s); } + bool operator<(value_type const& lhs, value_type const& rhs) { return std::tie(lhs.i, lhs.s) < std::tie(rhs.i, rhs.s); } + bool + operator>(value_type const& lhs, value_type const& rhs) + { return std::tie(lhs.i, lhs.s) > std::tie(rhs.i, rhs.s); } + + bool + operator<=(value_type const& lhs, value_type const& rhs) + { return std::tie(lhs.i, lhs.s) <= std::tie(rhs.i, rhs.s); } + + bool + operator>=(value_type const& lhs, value_type const& rhs) + { return std::tie(lhs.i, lhs.s) >= std::tie(rhs.i, rhs.s); } + } // namespace ns int main() diff --git a/libstdc++-v3/testsuite/20_util/optional/requirements.cc b/libstdc++-v3/testsuite/20_util/optional/requirements.cc index aab572f5795..d0f3ab6d773 100644 --- a/libstdc++-v3/testsuite/20_util/optional/requirements.cc +++ b/libstdc++-v3/testsuite/20_util/optional/requirements.cc @@ -257,3 +257,82 @@ int main() static_assert( *o == 33, "" ); } } + +using std::void_t; +using std::declval; +using std::true_type; +using std::false_type; + +template +struct is_eq_comparable : false_type {}; +template +struct is_eq_comparable() == declval())>> +: true_type {}; + +template +struct is_neq_comparable : false_type {}; +template +struct is_neq_comparable() != declval())>> +: true_type {}; + +template +struct is_lt_comparable : false_type {}; +template +struct is_lt_comparable() < declval())>> +: true_type {}; + +template +struct is_gt_comparable : false_type {}; +template +struct is_gt_comparable() > declval())>> +: true_type {}; + +template +struct is_le_comparable : false_type {}; +template +struct is_le_comparable() <= declval())>> +: true_type {}; + +template +struct is_ge_comparable : false_type {}; +template +struct is_ge_comparable() >= declval())>> +: true_type {}; + +using std::optional; + +static_assert(is_eq_comparable>::value, ""); +static_assert(is_neq_comparable>::value, ""); +static_assert(is_lt_comparable>::value, ""); +static_assert(is_gt_comparable>::value, ""); +static_assert(is_le_comparable>::value, ""); +static_assert(is_ge_comparable>::value, ""); + +struct JustEq {}; +bool operator==(const JustEq&, const JustEq&); + +static_assert(is_eq_comparable>::value, ""); +static_assert(!is_neq_comparable>::value, ""); +static_assert(!is_lt_comparable>::value, ""); +static_assert(!is_gt_comparable>::value, ""); +static_assert(!is_le_comparable>::value, ""); +static_assert(!is_ge_comparable>::value, ""); + +struct JustLt {}; +bool operator<(const JustLt&, const JustLt&); + +static_assert(!is_eq_comparable>::value, ""); +static_assert(!is_neq_comparable>::value, ""); +static_assert(is_lt_comparable>::value, ""); +static_assert(!is_gt_comparable>::value, ""); +static_assert(!is_le_comparable>::value, ""); +static_assert(!is_ge_comparable>::value, ""); + +static_assert(!std::is_assignable&, + optional>::value, ""); +static_assert(!std::is_assignable&, + JustLt>::value, ""); +static_assert(!std::is_assignable&, + optional&>::value, ""); +static_assert(!std::is_assignable&, + JustLt&>::value, ""); -- 2.30.2