Implement P0307R2, Making Optional Greater Equal Again.
authorVille Voutilainen <ville@gcc.gnu.org>
Wed, 13 Jul 2016 11:25:51 +0000 (14:25 +0300)
committerVille Voutilainen <ville@gcc.gnu.org>
Wed, 13 Jul 2016 11:25:51 +0000 (14:25 +0300)
* 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
libstdc++-v3/include/experimental/optional
libstdc++-v3/include/std/optional
libstdc++-v3/testsuite/20_util/optional/constexpr/relops/2.cc
libstdc++-v3/testsuite/20_util/optional/constexpr/relops/4.cc
libstdc++-v3/testsuite/20_util/optional/relops/1.cc
libstdc++-v3/testsuite/20_util/optional/relops/2.cc
libstdc++-v3/testsuite/20_util/optional/relops/3.cc
libstdc++-v3/testsuite/20_util/optional/relops/4.cc
libstdc++-v3/testsuite/20_util/optional/requirements.cc

index 17e612b265904f342c8b3f302b86bc50b70be0e5..f4d09963b57c734b85eb47e4f25c625627125114 100644 (file)
@@ -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  <jwakely@redhat.com>
 
        * include/bits/basic_string.h [_GLIBCXX_USE_CXX11_ABI] (_M_c_str):
index b6425b7d00ef86dedb04e043502b68966f947858..ea8f6fb1c64324f45073dea9601078852d0d9b13 100644 (file)
@@ -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<const _Tp&>().operator&()).
     */
   template<typename _Tp>
     struct _Has_addressof
index e9a86a44aa2ad64e0dac4e3532f29c35eca9acff..f1bb17c75dc937442d98d8ec592d9591c891d7a3 100644 (file)
@@ -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<const _Tp&>().operator&()).
     */
   template<typename _Tp>
     struct _Has_addressof
@@ -577,16 +577,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       template<typename _Up,
                enable_if_t<__and_<
+                          is_constructible<_Tp, _Up>,
+                          is_assignable<_Tp&, _Up>,
                           __not_<is_same<_Up, nullopt_t>>,
                           __not_<__is_optional<_Up>>>::value,
                         bool> = true>
         optional&
         operator=(_Up&& __u)
         {
-          static_assert(__and_<is_constructible<_Tp, _Up>,
-                              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<typename _Up,
                enable_if_t<__and_<
-                __not_<is_same<_Tp, _Up>>>::value,
-                          bool> = true>
+                          is_constructible<_Tp, _Up>,
+                          is_assignable<_Tp&, _Up>,
+                          __not_<is_same<_Tp, _Up>>>::value,
+                        bool> = true>
         optional&
         operator=(const optional<_Up>& __u)
         {
-          static_assert(__and_<is_constructible<_Tp, _Up>,
-                              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<typename _Up,
-               enable_if_t<__and_<
-                __not_<is_same<_Tp, _Up>>>::value,
-                          bool> = true>
+              enable_if_t<__and_<
+                          is_constructible<_Tp, _Up>,
+                          is_assignable<_Tp&, _Up>,
+                          __not_<is_same<_Tp, _Up>>>::value,
+                        bool> = true>
         optional&
         operator=(optional<_Up>&& __u)
         {
-          static_assert(__and_<is_constructible<_Tp, _Up>,
-                              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<typename _Tp>
+    using __optional_relop_t =
+    enable_if_t<is_convertible<_Tp, bool>::value, bool>;
+
   // [X.Y.8] Comparisons between optional values.
   template<typename _Tp>
-    constexpr bool
+    constexpr auto
     operator==(const optional<_Tp>& __lhs, const optional<_Tp>& __rhs)
+    -> __optional_relop_t<decltype(declval<_Tp>() == declval<_Tp>())>
     {
       return static_cast<bool>(__lhs) == static_cast<bool>(__rhs)
             && (!__lhs || *__lhs == *__rhs);
     }
 
   template<typename _Tp>
-    constexpr bool
+    constexpr auto
     operator!=(const optional<_Tp>& __lhs, const optional<_Tp>& __rhs)
-    { return !(__lhs == __rhs); }
+    -> __optional_relop_t<decltype(declval<_Tp>() != declval<_Tp>())>
+    {
+      return static_cast<bool>(__lhs) != static_cast<bool>(__rhs)
+       || (static_cast<bool>(__lhs) && *__lhs != *__rhs);
+    }
 
   template<typename _Tp>
-    constexpr bool
+    constexpr auto
     operator<(const optional<_Tp>& __lhs, const optional<_Tp>& __rhs)
+    -> __optional_relop_t<decltype(declval<_Tp>() < declval<_Tp>())>
     {
       return static_cast<bool>(__rhs) && (!__lhs || *__lhs < *__rhs);
     }
 
   template<typename _Tp>
-    constexpr bool
+    constexpr auto
     operator>(const optional<_Tp>& __lhs, const optional<_Tp>& __rhs)
-    { return __rhs < __lhs; }
+    -> __optional_relop_t<decltype(declval<_Tp>() > declval<_Tp>())>
+    {
+      return static_cast<bool>(__lhs) && (!__rhs || *__lhs > *__rhs);
+    }
 
   template<typename _Tp>
-    constexpr bool
+    constexpr auto
     operator<=(const optional<_Tp>& __lhs, const optional<_Tp>& __rhs)
-    { return !(__rhs < __lhs); }
+    -> __optional_relop_t<decltype(declval<_Tp>() <= declval<_Tp>())>
+    {
+      return !__lhs || (static_cast<bool>(__rhs) && *__lhs <= *__rhs);
+    }
 
   template<typename _Tp>
-    constexpr bool
+    constexpr auto
     operator>=(const optional<_Tp>& __lhs, const optional<_Tp>& __rhs)
-    { return !(__lhs < __rhs); }
+    -> __optional_relop_t<decltype(declval<_Tp>() >= declval<_Tp>())>
+    {
+      return !__rhs || (static_cast<bool>(__lhs) && *__lhs >= *__rhs);
+    }
 
   // [X.Y.9] Comparisons with nullopt.
   template<typename _Tp>
@@ -884,64 +897,76 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // [X.Y.10] Comparisons with value type.
   template<typename _Tp>
-    constexpr bool
+    constexpr auto
     operator==(const optional<_Tp>& __lhs, const _Tp& __rhs)
+    -> __optional_relop_t<decltype(declval<_Tp>() == declval<_Tp>())>
     { return __lhs && *__lhs == __rhs; }
 
   template<typename _Tp>
-    constexpr bool
+    constexpr auto
     operator==(const _Tp& __lhs, const optional<_Tp>& __rhs)
+    -> __optional_relop_t<decltype(declval<_Tp>() == declval<_Tp>())>
     { return __rhs && __lhs == *__rhs; }
 
   template<typename _Tp>
-    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<decltype(declval<_Tp>() != declval<_Tp>())>
+    { return !__lhs || *__lhs != __rhs; }
 
   template<typename _Tp>
-    constexpr bool
+    constexpr auto
     operator!=(const _Tp& __lhs, const optional<_Tp>& __rhs)
-    { return !__rhs || !(__lhs == *__rhs); }
+    -> __optional_relop_t<decltype(declval<_Tp>() != declval<_Tp>())>
+    { return !__rhs || __lhs != *__rhs; }
 
   template<typename _Tp>
-    constexpr bool
+    constexpr auto
     operator<(const optional<_Tp>& __lhs, const _Tp& __rhs)
+    -> __optional_relop_t<decltype(declval<_Tp>() < declval<_Tp>())>
     { return !__lhs || *__lhs < __rhs; }
 
   template<typename _Tp>
-    constexpr bool
+    constexpr auto
     operator<(const _Tp& __lhs, const optional<_Tp>& __rhs)
+    -> __optional_relop_t<decltype(declval<_Tp>() < declval<_Tp>())>
     { return __rhs && __lhs < *__rhs; }
 
   template<typename _Tp>
-    constexpr bool
+    constexpr auto
     operator>(const optional<_Tp>& __lhs, const _Tp& __rhs)
-    { return __lhs && __rhs < *__lhs; }
+    -> __optional_relop_t<decltype(declval<_Tp>() > declval<_Tp>())>
+    { return __lhs && *__lhs > __rhs; }
 
   template<typename _Tp>
-    constexpr bool
+    constexpr auto
     operator>(const _Tp& __lhs, const optional<_Tp>& __rhs)
-    { return !__rhs || *__rhs < __lhs; }
+    -> __optional_relop_t<decltype(declval<_Tp>() > declval<_Tp>())>
+    { return !__rhs || __lhs > *__rhs; }
 
   template<typename _Tp>
-    constexpr bool
+    constexpr auto
     operator<=(const optional<_Tp>& __lhs, const _Tp& __rhs)
-    { return !__lhs || !(__rhs < *__lhs); }
+    -> __optional_relop_t<decltype(declval<_Tp>() <= declval<_Tp>())>
+    { return !__lhs || *__lhs <= __rhs; }
 
   template<typename _Tp>
-    constexpr bool
+    constexpr auto
     operator<=(const _Tp& __lhs, const optional<_Tp>& __rhs)
-    { return __rhs && !(*__rhs < __lhs); }
+    -> __optional_relop_t<decltype(declval<_Tp>() <= declval<_Tp>())>
+    { return __rhs && __lhs <= *__rhs; }
 
   template<typename _Tp>
-    constexpr bool
+    constexpr auto
     operator>=(const optional<_Tp>& __lhs, const _Tp& __rhs)
-    { return __lhs && !(*__lhs < __rhs); }
+    -> __optional_relop_t<decltype(declval<_Tp>() >= declval<_Tp>())>
+    { return __lhs && *__lhs >= __rhs; }
 
   template<typename _Tp>
-    constexpr bool
+    constexpr auto
     operator>=(const _Tp& __lhs, const optional<_Tp>& __rhs)
-    { return !__rhs || !(__lhs < *__rhs); }
+    -> __optional_relop_t<decltype(declval<_Tp>() >= declval<_Tp>())>
+    { return !__rhs || __lhs >= *__rhs; }
 
   // [X.Y.11]
   template<typename _Tp>
index 9aa9273255ba38bc39e7099ac5a09247a389f457..0ce00c1052f7250820be946fe29ffb7200d7590b 100644 (file)
@@ -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()
index 15130d4efbda55b800fa25e882ac230719c2d6ad..d6294ad9d7cbea73b08049c45facf5a92e6f82d8 100644 (file)
@@ -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()
index 62770329effe66345a852739a08c7186f0ac0ca5..1315902239efc730c6be85ece3caa2c25fa1e002 100644 (file)
@@ -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()
index 65071c04bce8cf21ba2414de83ffb590214b1a60..135126576728a97f429486e7c1464469adf7f1d1 100644 (file)
@@ -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()
index 2fd9e8bc1e7409dd6d34ced31d0db3f48959af5b..95fde3b4797d32d921be01a1199ada605fe79db8 100644 (file)
@@ -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()
index 363e633a40c61e0c0491ba6c4caa46908252a856..78d0eb8b2d032167e09dd4095749338465e02d86 100644 (file)
@@ -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()
index aab572f5795f65496070a0a50967edf151a1a8d6..d0f3ab6d773734407c366714b71915f1899609d5 100644 (file)
@@ -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 <class T, class = void>
+struct is_eq_comparable : false_type {};
+template <class T>
+struct is_eq_comparable<T, void_t<decltype(declval<T>() == declval<T>())>>
+: true_type {};
+
+template <class T, class = void>
+struct is_neq_comparable : false_type {};
+template <class T>
+struct is_neq_comparable<T, void_t<decltype(declval<T>() != declval<T>())>>
+: true_type {};
+
+template <class T, class = void>
+struct is_lt_comparable : false_type {};
+template <class T>
+struct is_lt_comparable<T, void_t<decltype(declval<T>() < declval<T>())>>
+: true_type {};
+
+template <class T, class = void>
+struct is_gt_comparable : false_type {};
+template <class T>
+struct is_gt_comparable<T, void_t<decltype(declval<T>() > declval<T>())>>
+: true_type {};
+
+template <class T, class = void>
+struct is_le_comparable : false_type {};
+template <class T>
+struct is_le_comparable<T, void_t<decltype(declval<T>() <= declval<T>())>>
+: true_type {};
+
+template <class T, class = void>
+struct is_ge_comparable : false_type {};
+template <class T>
+struct is_ge_comparable<T, void_t<decltype(declval<T>() >= declval<T>())>>
+: true_type {};
+
+using std::optional;
+
+static_assert(is_eq_comparable<optional<int>>::value, "");
+static_assert(is_neq_comparable<optional<int>>::value, "");
+static_assert(is_lt_comparable<optional<int>>::value, "");
+static_assert(is_gt_comparable<optional<int>>::value, "");
+static_assert(is_le_comparable<optional<int>>::value, "");
+static_assert(is_ge_comparable<optional<int>>::value, "");
+
+struct JustEq {};
+bool operator==(const JustEq&, const JustEq&);
+
+static_assert(is_eq_comparable<optional<JustEq>>::value, "");
+static_assert(!is_neq_comparable<optional<JustEq>>::value, "");
+static_assert(!is_lt_comparable<optional<JustEq>>::value, "");
+static_assert(!is_gt_comparable<optional<JustEq>>::value, "");
+static_assert(!is_le_comparable<optional<JustEq>>::value, "");
+static_assert(!is_ge_comparable<optional<JustEq>>::value, "");
+
+struct JustLt {};
+bool operator<(const JustLt&, const JustLt&);
+
+static_assert(!is_eq_comparable<optional<JustLt>>::value, "");
+static_assert(!is_neq_comparable<optional<JustLt>>::value, "");
+static_assert(is_lt_comparable<optional<JustLt>>::value, "");
+static_assert(!is_gt_comparable<optional<JustLt>>::value, "");
+static_assert(!is_le_comparable<optional<JustLt>>::value, "");
+static_assert(!is_ge_comparable<optional<JustLt>>::value, "");
+
+static_assert(!std::is_assignable<optional<JustEq>&,
+             optional<JustLt>>::value, "");
+static_assert(!std::is_assignable<optional<JustEq>&,
+             JustLt>::value, "");
+static_assert(!std::is_assignable<optional<JustEq>&,
+             optional<JustLt>&>::value, "");
+static_assert(!std::is_assignable<optional<JustEq>&,
+             JustLt&>::value, "");