variant (std::get, operator==): Implement constexpr comparison and get<>.
authorTim Shen <timshen@google.com>
Tue, 6 Dec 2016 11:20:13 +0000 (11:20 +0000)
committerTim Shen <timshen@gcc.gnu.org>
Tue, 6 Dec 2016 11:20:13 +0000 (11:20 +0000)
* include/std/variant (std::get, operator==): Implement constexpr
comparison and get<>.
* testsuite/20_util/variant/compile.cc: Tests.

From-SVN: r243293

libstdc++-v3/ChangeLog
libstdc++-v3/include/std/variant
libstdc++-v3/testsuite/20_util/variant/compile.cc

index ecda02675cf1bc08a5791fb08322d5e80ec4626a..8a3ab43d665b5f2ca12a131fe8b70620515c2f02 100644 (file)
@@ -1,3 +1,9 @@
+2016-12-07  Tim Shen  <timshen@google.com>
+
+       * include/std/variant (std::get, operator==): Implement constexpr
+       comparison and get<>.
+       * testsuite/20_util/variant/compile.cc: Tests.
+
 2016-12-07  Tim Shen  <timshen@google.com>
 
        * include/std/variant (__erased_use_alloc_ctor,
index 32c0dc3c07ae2303286a063ea868cc54aaaada0c..a961a058a5693ad81bb2b54ddca0bcd7b56f58f4 100644 (file)
@@ -41,6 +41,7 @@
 #include <bits/functexcept.h>
 #include <bits/move.h>
 #include <bits/functional_hash.h>
+#include <ext/aligned_buffer.h>
 
 namespace std _GLIBCXX_VISIBILITY(default)
 {
@@ -153,33 +154,60 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Alternative>
     using __storage = _Alternative;
 
-  template<typename _Type, bool __is_literal = std::is_literal_type_v<_Type>>
+  // _Uninitialized<T> is guaranteed to be a literal type, even if T is not.
+  // We have to do this, because [basic.types]p10.5.3 (n4606) is not implemented
+  // yet. When it's implemented, _Uninitialized<T> can be changed to the alias
+  // to T, therefore equivalent to being removed entirely.
+  //
+  // Another reason we may not want to remove _Uninitialzied<T> may be that, we
+  // want _Uninitialized<T> to be trivially destructible, no matter whether T
+  // is; but we will see.
+  template<typename _Type, bool = std::is_literal_type_v<_Type>>
     struct _Uninitialized;
 
   template<typename _Type>
     struct _Uninitialized<_Type, true>
     {
-      constexpr _Uninitialized() = default;
-
       template<typename... _Args>
       constexpr _Uninitialized(in_place_index_t<0>, _Args&&... __args)
       : _M_storage(std::forward<_Args>(__args)...)
       { }
 
+      constexpr const _Type& _M_get() const &
+      { return _M_storage; }
+
+      constexpr _Type& _M_get() &
+      { return _M_storage; }
+
+      constexpr const _Type&& _M_get() const &&
+      { return std::move(_M_storage); }
+
+      constexpr _Type&& _M_get() &&
+      { return std::move(_M_storage); }
+
       _Type _M_storage;
     };
 
   template<typename _Type>
     struct _Uninitialized<_Type, false>
     {
-      constexpr _Uninitialized() = default;
-
       template<typename... _Args>
       constexpr _Uninitialized(in_place_index_t<0>, _Args&&... __args)
       { ::new (&_M_storage) _Type(std::forward<_Args>(__args)...); }
 
-      typename std::aligned_storage<sizeof(_Type), alignof(_Type)>::type
-         _M_storage;
+      const _Type& _M_get() const &
+      { return *_M_storage._M_ptr(); }
+
+      _Type& _M_get() &
+      { return *_M_storage._M_ptr(); }
+
+      const _Type&& _M_get() const &&
+      { return std::move(*_M_storage._M_ptr()); }
+
+      _Type&& _M_get() &&
+      { return std::move(*_M_storage._M_ptr()); }
+
+      __gnu_cxx::__aligned_membuf<_Type> _M_storage;
     };
 
   // Given a qualified storage type, return the desired reference.
@@ -194,6 +222,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        *static_cast<_Storage*>(__ptr));
     }
 
+  template<typename _Union>
+    constexpr decltype(auto) __get(in_place_index_t<0>, _Union&& __u)
+    { return std::forward<_Union>(__u)._M_first._M_get(); }
+
+  template<size_t _Np, typename _Union>
+    constexpr decltype(auto) __get(in_place_index_t<_Np>, _Union&& __u)
+    { return __get(in_place_index<_Np-1>, std::forward<_Union>(__u)._M_rest); }
+
+  // Returns the typed storage for __v.
+  template<size_t _Np, typename _Variant>
+    constexpr decltype(auto) __get(_Variant&& __v)
+    {
+      return __get(std::in_place_index<_Np>, std::forward<_Variant>(__v)._M_u);
+    }
+
   // Various functions as "vtable" entries, where those vtables are used by
   // polymorphic operations.
   template<typename _Lhs, typename _Rhs>
@@ -201,13 +244,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     __erased_ctor(void* __lhs, void* __rhs)
     { ::new (__lhs) decay_t<_Lhs>(__get_alternative<_Rhs>(__rhs)); }
 
-  // TODO: Find a potential chance to reuse this accross the project.
-  template<typename _Tp>
+  template<typename _Variant, size_t _Np>
     constexpr void
-    __erased_dtor(void* __ptr)
+    __erased_dtor(_Variant&& __v)
     {
-      using _Storage = decay_t<_Tp>;
-      static_cast<_Storage*>(__ptr)->~_Storage();
+      auto&& __element = __get<_Np>(std::forward<_Variant>(__v));
+      using _Type = std::remove_reference_t<decltype(__element)>;
+      __element.~_Type();
     }
 
   template<typename _Lhs, typename _Rhs>
@@ -223,90 +266,108 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       swap(__get_alternative<_Lhs>(__lhs), __get_alternative<_Rhs>(__rhs));
     }
 
-  template<typename _Lhs, typename _Rhs>
+  template<typename _Variant, size_t _Np>
     constexpr bool
-    __erased_equal_to(void* __lhs, void* __rhs)
-    { return __get_alternative<_Lhs>(__lhs) == __get_alternative<_Rhs>(__rhs); }
+    __erased_equal_to(_Variant&& __lhs, _Variant&& __rhs)
+    {
+      return __get<_Np>(std::forward<_Variant>(__lhs))
+         == __get<_Np>(std::forward<_Variant>(__rhs));
+    }
 
-  template<typename _Lhs, typename _Rhs>
+  template<typename _Variant, size_t _Np>
     constexpr bool
-    __erased_less_than(void* __lhs, void* __rhs)
-    { return __get_alternative<_Lhs>(__lhs) < __get_alternative<_Rhs>(__rhs); }
+    __erased_less_than(const _Variant& __lhs, const _Variant& __rhs)
+    {
+      return __get<_Np>(std::forward<_Variant>(__lhs))
+         < __get<_Np>(std::forward<_Variant>(__rhs));
+    }
 
   template<typename _Tp>
     constexpr size_t
     __erased_hash(void* __t)
     { return std::hash<decay_t<_Tp>>{}(__get_alternative<_Tp>(__t)); }
 
+  // Defines members and ctors.
   template<typename... _Types>
-    struct _Variant_base;
+    union _Variadic_union { };
 
-  template<typename... _Types>
-    struct _Variant_storage
-    { constexpr _Variant_storage() = default; };
-
-  // Use recursive unions to implement a trivially destructible variant.
   template<typename _First, typename... _Rest>
-    struct _Variant_storage<_First, _Rest...>
+    union _Variadic_union<_First, _Rest...>
     {
-      constexpr _Variant_storage() = default;
+      constexpr _Variadic_union() : _M_rest() { }
+
+      template<typename... _Args>
+       constexpr _Variadic_union(in_place_index_t<0>, _Args&&... __args)
+       : _M_first(in_place_index<0>, std::forward<_Args>(__args)...)
+       { }
+
+      template<size_t _Np, typename... _Args>
+       constexpr _Variadic_union(in_place_index_t<_Np>, _Args&&... __args)
+       : _M_rest(in_place_index<_Np-1>, std::forward<_Args>(__args)...)
+       { }
+
+      _Uninitialized<_First> _M_first;
+      _Variadic_union<_Rest...> _M_rest;
+    };
+
+  // Defines index and the dtor, possibly trivial.
+  template<bool __trivially_destructible, typename... _Types>
+    struct _Variant_storage;
+
+  template<typename... _Types>
+    struct _Variant_storage<false, _Types...>
+    {
+      template<size_t... __indices>
+       static constexpr void (*_S_vtable[])(const _Variant_storage&) =
+           { &__erased_dtor<const _Variant_storage&, __indices>... };
+
+      constexpr _Variant_storage() : _M_index(variant_npos) { }
 
       template<size_t _Np, typename... _Args>
        constexpr _Variant_storage(in_place_index_t<_Np>, _Args&&... __args)
-       : _M_union(in_place_index<_Np>, std::forward<_Args>(__args)...)
+       : _M_u(in_place_index<_Np>, std::forward<_Args>(__args)...),
+       _M_index(_Np)
        { }
 
-      ~_Variant_storage() = default;
+      template<size_t... __indices>
+       constexpr void _M_destroy_impl(std::index_sequence<__indices...>)
+       {
+         if (_M_index != variant_npos)
+           _S_vtable<__indices...>[_M_index](*this);
+       }
 
-      constexpr void*
-      _M_storage() const
-      {
-       return const_cast<void*>(
-         static_cast<const void*>(std::addressof(_M_union._M_first._M_storage)));
-      }
+      ~_Variant_storage()
+      { _M_destroy_impl(std::index_sequence_for<_Types...>{}); }
 
-      union _Union
-      {
-       constexpr _Union() {};
-
-       template<typename... _Args>
-         constexpr _Union(in_place_index_t<0>, _Args&&... __args)
-         : _M_first(in_place_index<0>, std::forward<_Args>(__args)...)
-         { }
-
-       template<size_t _Np, typename... _Args,
-                typename = enable_if_t<0 < _Np && _Np < sizeof...(_Rest) + 1>>
-         constexpr _Union(in_place_index_t<_Np>, _Args&&... __args)
-         : _M_rest(in_place_index<_Np - 1>, std::forward<_Args>(__args)...)
-         { }
-
-       _Uninitialized<__storage<_First>> _M_first;
-       _Variant_storage<_Rest...> _M_rest;
-      } _M_union;
+      _Variadic_union<_Types...> _M_u;
+      size_t _M_index;
     };
 
-  template<typename _Derived, bool __is_trivially_destructible>
-    struct _Dtor_mixin
+  template<typename... _Types>
+    struct _Variant_storage<true, _Types...>
     {
-      ~_Dtor_mixin()
-      { static_cast<_Derived*>(this)->_M_destroy(); }
-    };
+      constexpr _Variant_storage() : _M_index(variant_npos) { }
 
-  template<typename _Derived>
-    struct _Dtor_mixin<_Derived, true>
-    {
-      ~_Dtor_mixin() = default;
+      template<size_t _Np, typename... _Args>
+       constexpr _Variant_storage(in_place_index_t<_Np>, _Args&&... __args)
+       : _M_u(in_place_index<_Np>, std::forward<_Args>(__args)...),
+       _M_index(_Np)
+       { }
+
+      _Variadic_union<_Types...> _M_u;
+      size_t _M_index;
     };
 
   // Helps SFINAE on special member functions. Otherwise it can live in variant
   // class.
   template<typename... _Types>
     struct _Variant_base :
-      _Variant_storage<_Types...>,
-      _Dtor_mixin<_Variant_base<_Types...>,
-                 __and_<std::is_trivially_destructible<_Types>...>::value>
+      _Variant_storage<(std::is_trivially_destructible_v<_Types> && ...),
+                       _Types...>
     {
-      using _Storage = _Variant_storage<_Types...>;
+      using _Storage =
+         _Variant_storage<(std::is_trivially_destructible_v<_Types> && ...),
+                           _Types...>;
 
       constexpr
       _Variant_base()
@@ -315,7 +376,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       : _Variant_base(in_place_index<0>) { }
 
       _Variant_base(const _Variant_base& __rhs)
-      : _Storage(), _M_index(__rhs._M_index)
       {
        if (__rhs._M_valid())
          {
@@ -323,31 +383,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
              { &__erased_ctor<__storage<_Types>&,
                               const __storage<_Types>&>... };
            _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage());
+           this->_M_index = __rhs._M_index;
          }
       }
 
       _Variant_base(_Variant_base&& __rhs)
       noexcept(__and_<is_nothrow_move_constructible<_Types>...>::value)
-      : _Storage(), _M_index(__rhs._M_index)
       {
        if (__rhs._M_valid())
          {
            static constexpr void (*_S_vtable[])(void*, void*) =
              { &__erased_ctor<__storage<_Types>&, __storage<_Types>&&>... };
            _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage());
+           this->_M_index = __rhs._M_index;
          }
       }
 
       template<size_t _Np, typename... _Args>
        constexpr explicit
        _Variant_base(in_place_index_t<_Np> __i, _Args&&... __args)
-       : _Storage(__i, std::forward<_Args>(__args)...), _M_index(_Np)
+       : _Storage(__i, std::forward<_Args>(__args)...)
        { }
 
       _Variant_base&
       operator=(const _Variant_base& __rhs)
       {
-       if (_M_index == __rhs._M_index)
+       if (this->_M_index == __rhs._M_index)
          {
            if (__rhs._M_valid())
              {
@@ -367,11 +428,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
              }
            __catch (...)
              {
-               _M_index = variant_npos;
+               this->_M_index = variant_npos;
                __throw_exception_again;
              }
          }
-       __glibcxx_assert(_M_index == __rhs._M_index);
+       __glibcxx_assert(this->_M_index == __rhs._M_index);
        return *this;
       }
 
@@ -380,7 +441,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       noexcept(__and_<is_nothrow_move_constructible<_Types>...,
                      is_nothrow_move_assignable<_Types>...>::value)
       {
-       if (_M_index == __rhs._M_index)
+       if (this->_M_index == __rhs._M_index)
          {
            if (__rhs._M_valid())
              {
@@ -399,32 +460,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
              }
            __catch (...)
              {
-               _M_index = variant_npos;
+               this->_M_index = variant_npos;
                __throw_exception_again;
              }
          }
        return *this;
       }
 
-      void _M_destroy()
+      void*
+      _M_storage() const
       {
-       if (_M_valid())
-         {
-           static constexpr void (*_S_vtable[])(void*) =
-             { &__erased_dtor<__storage<_Types>&>... };
-           _S_vtable[this->_M_index](_M_storage());
-         }
+       return const_cast<void*>(static_cast<const void*>(
+           std::addressof(_Storage::_M_u)));
       }
 
-      constexpr void*
-      _M_storage() const
-      { return _Storage::_M_storage(); }
-
       constexpr bool
       _M_valid() const noexcept
-      { return _M_index != variant_npos; }
-
-      size_t _M_index;
+      { return this->_M_index != variant_npos; }
     };
 
   // For how many times does _Tp appear in _Tuple?
@@ -489,15 +541,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     void* __get_storage(_Variant&& __v)
     { return __v._M_storage(); }
 
-  // Returns the reference to the desired alternative.
-  // It is as unsafe as a reinterpret_cast.
-  template<typename _Tp, typename _Variant>
-    decltype(auto) __access(_Variant&& __v)
-    {
-      return __get_alternative<__reserved_type_map<_Variant&&, __storage<_Tp>>>(
-       __get_storage(std::forward<_Variant>(__v)));
-    }
-
   // A helper used to create variadic number of _To types.
   template<typename _From, typename _To>
     using _To_type = _To;
@@ -597,9 +640,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        _S_apply_all_alts(_Array_type& __vtable, index_sequence<__indices...>)
        { (_S_apply_single_alt<__indices>(__vtable._M_arr[__indices]), ...); }
 
-      template<size_t __index>
+      template<size_t __index, typename _Tp>
        static constexpr void
-       _S_apply_single_alt(auto& __element)
+       _S_apply_single_alt(_Tp& __element)
        {
          using _Alternative = variant_alternative_t<__index, decay_t<_First>>;
          using _Qualified_storage = __reserved_type_map<
@@ -655,23 +698,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     }
 
   template<size_t _Np, typename... _Types>
-    variant_alternative_t<_Np, variant<_Types...>>&
+    constexpr variant_alternative_t<_Np, variant<_Types...>>&
     get(variant<_Types...>&);
 
   template<size_t _Np, typename... _Types>
-    variant_alternative_t<_Np, variant<_Types...>>&&
+    constexpr variant_alternative_t<_Np, variant<_Types...>>&&
     get(variant<_Types...>&&);
 
   template<size_t _Np, typename... _Types>
-    variant_alternative_t<_Np, variant<_Types...>> const&
+    constexpr variant_alternative_t<_Np, variant<_Types...>> const&
     get(const variant<_Types...>&);
 
   template<size_t _Np, typename... _Types>
-    variant_alternative_t<_Np, variant<_Types...>> const&&
+    constexpr variant_alternative_t<_Np, variant<_Types...>> const&&
     get(const variant<_Types...>&&);
 
   template<typename _Tp, typename... _Types>
-    inline _Tp& get(variant<_Types...>& __v)
+    constexpr inline _Tp& get(variant<_Types...>& __v)
     {
       static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>,
                    "T should occur for exactly once in alternatives");
@@ -680,7 +723,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     }
 
   template<typename _Tp, typename... _Types>
-    inline _Tp&& get(variant<_Types...>&& __v)
+    constexpr inline _Tp&& get(variant<_Types...>&& __v)
     {
       static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>,
                    "T should occur for exactly once in alternatives");
@@ -690,7 +733,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     }
 
   template<typename _Tp, typename... _Types>
-    inline const _Tp& get(const variant<_Types...>& __v)
+    constexpr inline const _Tp& get(const variant<_Types...>& __v)
     {
       static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>,
                    "T should occur for exactly once in alternatives");
@@ -699,7 +742,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     }
 
   template<typename _Tp, typename... _Types>
-    inline const _Tp&& get(const variant<_Types...>&& __v)
+    constexpr inline const _Tp&& get(const variant<_Types...>&& __v)
     {
       static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>,
                    "T should occur for exactly once in alternatives");
@@ -709,7 +752,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     }
 
   template<size_t _Np, typename... _Types>
-    inline add_pointer_t<variant_alternative_t<_Np, variant<_Types...>>>
+    constexpr inline
+    add_pointer_t<variant_alternative_t<_Np, variant<_Types...>>>
     get_if(variant<_Types...>* __ptr) noexcept
     {
       using _Alternative_type = variant_alternative_t<_Np, variant<_Types...>>;
@@ -717,12 +761,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                    "The index should be in [0, number of alternatives)");
       static_assert(!is_void_v<_Alternative_type>, "_Tp should not be void");
       if (__ptr && __ptr->index() == _Np)
-       return &__detail::__variant::__access<_Alternative_type>(*__ptr);
+       return &__detail::__variant::__get<_Np>(*__ptr);
       return nullptr;
     }
 
   template<size_t _Np, typename... _Types>
-    inline add_pointer_t<const variant_alternative_t<_Np, variant<_Types...>>>
+    constexpr inline
+    add_pointer_t<const variant_alternative_t<_Np, variant<_Types...>>>
     get_if(const variant<_Types...>* __ptr) noexcept
     {
       using _Alternative_type = variant_alternative_t<_Np, variant<_Types...>>;
@@ -730,12 +775,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                    "The index should be in [0, number of alternatives)");
       static_assert(!is_void_v<_Alternative_type>, "_Tp should not be void");
       if (__ptr && __ptr->index() == _Np)
-       return &__detail::__variant::__access<_Alternative_type>(*__ptr);
+       return &__detail::__variant::__get<_Np>(*__ptr);
       return nullptr;
     }
 
   template<typename _Tp, typename... _Types>
-    inline add_pointer_t<_Tp> get_if(variant<_Types...>* __ptr) noexcept
+    constexpr inline add_pointer_t<_Tp>
+    get_if(variant<_Types...>* __ptr) noexcept
     {
       static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>,
                    "T should occur for exactly once in alternatives");
@@ -744,7 +790,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     }
 
   template<typename _Tp, typename... _Types>
-    inline add_pointer_t<const _Tp> get_if(const variant<_Types...>* __ptr)
+    constexpr inline add_pointer_t<const _Tp>
+    get_if(const variant<_Types...>* __ptr)
     noexcept
     {
       static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>,
@@ -754,64 +801,36 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     }
 
   template<typename... _Types>
-    bool operator==(const variant<_Types...>& __lhs,
-                   const variant<_Types...>& __rhs)
+    constexpr bool operator==(const variant<_Types...>& __lhs,
+                             const variant<_Types...>& __rhs)
     {
-      if (__lhs.index() != __rhs.index())
-       return false;
-
-      if (__lhs.valueless_by_exception())
-       return true;
-
-      using __detail::__variant::__storage;
-      static constexpr bool (*_S_vtable[])(void*, void*) =
-       { &__detail::__variant::__erased_equal_to<
-         const __storage<_Types>&, const __storage<_Types>&>... };
-      return _S_vtable[__lhs.index()](
-         __detail::__variant::__get_storage(__lhs),
-         __detail::__variant::__get_storage(__rhs));
+      return __lhs._M_equal_to(__rhs, std::index_sequence_for<_Types...>{});
     }
 
   template<typename... _Types>
-    inline bool
+    constexpr inline bool
     operator!=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs)
     { return !(__lhs == __rhs); }
 
   template<typename... _Types>
-    inline bool
+    constexpr inline bool
     operator<(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs)
     {
-      if (__lhs.index() < __rhs.index())
-       return true;
-
-      if (__lhs.index() > __rhs.index())
-       return false;
-
-      if (__lhs.valueless_by_exception())
-       return false;
-
-      using __detail::__variant::__storage;
-      static constexpr bool (*_S_vtable[])(void*, void*) =
-       { &__detail::__variant::__erased_less_than<
-           const __storage<_Types>&,
-           const __storage<_Types>&>... };
-      return _S_vtable[__lhs.index()](
-         __detail::__variant::__get_storage(__lhs),
-         __detail::__variant::__get_storage(__rhs));
+      return __lhs._M_less_than(__rhs, std::index_sequence_for<_Types...>{});
     }
 
   template<typename... _Types>
-    inline bool
+    constexpr inline bool
     operator>(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs)
     { return __rhs < __lhs; }
 
   template<typename... _Types>
-    inline bool
+    constexpr inline bool
     operator<=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs)
     { return !(__lhs > __rhs); }
 
   template<typename... _Types>
-    inline bool
+    constexpr inline bool
     operator>=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs)
     { return !(__lhs < __rhs); }
 
@@ -1102,60 +1121,120 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          }
       }
 
+    private:
+      template<size_t... __indices>
+       static constexpr bool
+       (*_S_equal_to_vtable[])(const variant&, const variant&) =
+         { &__detail::__variant::__erased_equal_to<
+           const variant&, __indices>... };
+
+      template<size_t... __indices>
+       static constexpr bool
+       (*_S_less_than_vtable[])(const variant&, const variant&) =
+         { &__detail::__variant::__erased_less_than<
+             const variant&, __indices>... };
+
+      template<size_t... __indices>
+       constexpr bool
+       _M_equal_to(const variant& __rhs,
+                   std::index_sequence<__indices...>) const
+       {
+         if (this->index() != __rhs.index())
+           return false;
+
+         if (this->valueless_by_exception())
+           return true;
+
+         return _S_equal_to_vtable<__indices...>[this->index()](*this, __rhs);
+       }
+
+      template<size_t... __indices>
+       constexpr inline bool
+       _M_less_than(const variant& __rhs,
+                    std::index_sequence<__indices...>) const
+       {
+         auto __lhs_index = this->index();
+         auto __rhs_index = __rhs.index();
+
+         if (__lhs_index < __rhs_index)
+           return true;
+
+         if (__lhs_index > __rhs_index)
+           return false;
+
+         if (this->valueless_by_exception())
+           return false;
+
+         return _S_less_than_vtable<__indices...>[__lhs_index](*this, __rhs);
+       }
+
+      template<size_t _Np, typename _Vp>
+       friend constexpr decltype(auto) __detail::__variant::
+#if _GLIBCXX_INLINE_VERSION
+        __7:: // Required due to PR c++/59256
+#endif
+       __get(_Vp&& __v);
+
       template<typename _Vp>
        friend void* __detail::__variant::
 #if _GLIBCXX_INLINE_VERSION
         __7:: // Required due to PR c++/59256
 #endif
         __get_storage(_Vp&& __v);
+
+      template<typename... _Tp>
+       friend constexpr bool
+       operator==(const variant<_Tp...>& __lhs,
+                  const variant<_Tp...>& __rhs);
+
+      template<typename... _Tp>
+       friend constexpr bool
+       operator<(const variant<_Tp...>& __lhs,
+                 const variant<_Tp...>& __rhs);
     };
 
   template<size_t _Np, typename... _Types>
-    variant_alternative_t<_Np, variant<_Types...>>&
+    constexpr variant_alternative_t<_Np, variant<_Types...>>&
     get(variant<_Types...>& __v)
     {
       static_assert(_Np < sizeof...(_Types),
                    "The index should be in [0, number of alternatives)");
       if (__v.index() != _Np)
        __throw_bad_variant_access("Unexpected index");
-      return __detail::__variant::__access<
-       variant_alternative_t<_Np, variant<_Types...>>>(__v);
+      return __detail::__variant::__get<_Np>(__v);
     }
 
   template<size_t _Np, typename... _Types>
-    variant_alternative_t<_Np, variant<_Types...>>&&
+    constexpr variant_alternative_t<_Np, variant<_Types...>>&&
     get(variant<_Types...>&& __v)
     {
       static_assert(_Np < sizeof...(_Types),
                    "The index should be in [0, number of alternatives)");
       if (__v.index() != _Np)
        __throw_bad_variant_access("Unexpected index");
-      return __detail::__variant::__access<
-       variant_alternative_t<_Np, variant<_Types...>>>(std::move(__v));
+      return __detail::__variant::__get<_Np>(std::move(__v));
     }
 
   template<size_t _Np, typename... _Types>
-    const variant_alternative_t<_Np, variant<_Types...>>&
+    constexpr const variant_alternative_t<_Np, variant<_Types...>>&
     get(const variant<_Types...>& __v)
     {
       static_assert(_Np < sizeof...(_Types),
                    "The index should be in [0, number of alternatives)");
       if (__v.index() != _Np)
        __throw_bad_variant_access("Unexpected index");
-      return __detail::__variant::__access<
-       variant_alternative_t<_Np, variant<_Types...>>>(__v);
+      return __detail::__variant::__get<_Np>(__v);
     }
 
   template<size_t _Np, typename... _Types>
-    const variant_alternative_t<_Np, variant<_Types...>>&&
+    constexpr const variant_alternative_t<_Np, variant<_Types...>>&&
     get(const variant<_Types...>&& __v)
     {
       static_assert(_Np < sizeof...(_Types),
                    "The index should be in [0, number of alternatives)");
       if (__v.index() != _Np)
        __throw_bad_variant_access("Unexpected index");
-      return __detail::__variant::__access<
-       variant_alternative_t<_Np, variant<_Types...>>>(std::move(__v));
+      return __detail::__variant::__get<_Np>(std::move(__v));
     }
 
   template<typename _Visitor, typename... _Variants>
index a67b651586b5fff55529b5b1544ebbe4479fabe2..ab8ada2e9191816d78027e359785e6252b507da4 100644 (file)
@@ -51,6 +51,14 @@ struct DefaultNoexcept
   DefaultNoexcept& operator=(DefaultNoexcept&&) noexcept = default;
 };
 
+struct nonliteral
+{
+  nonliteral() { }
+
+  bool operator<(const nonliteral&) const;
+  bool operator==(const nonliteral&) const;
+};
+
 void default_ctor()
 {
   static_assert(is_default_constructible_v<variant<int, string>>, "");
@@ -175,22 +183,40 @@ void test_get()
 void test_relational()
 {
   {
-    const variant<int, string> a, b;
-    (void)(a < b);
-    (void)(a > b);
-    (void)(a <= b);
-    (void)(a == b);
-    (void)(a != b);
-    (void)(a >= b);
+    constexpr variant<int, nonliteral> a(42), b(43);
+    static_assert((a < b), "");
+    static_assert(!(a > b), "");
+    static_assert((a <= b), "");
+    static_assert(!(a == b), "");
+    static_assert((a != b), "");
+    static_assert(!(a >= b), "");
   }
   {
-    const monostate a, b;
-    (void)(a < b);
-    (void)(a > b);
-    (void)(a <= b);
-    (void)(a == b);
-    (void)(a != b);
-    (void)(a >= b);
+    constexpr variant<int, nonliteral> a(42), b(42);
+    static_assert(!(a < b), "");
+    static_assert(!(a > b), "");
+    static_assert((a <= b), "");
+    static_assert((a == b), "");
+    static_assert(!(a != b), "");
+    static_assert((a >= b), "");
+  }
+  {
+    constexpr variant<int, nonliteral> a(43), b(42);
+    static_assert(!(a < b), "");
+    static_assert((a > b), "");
+    static_assert(!(a <= b), "");
+    static_assert(!(a == b), "");
+    static_assert((a != b), "");
+    static_assert((a >= b), "");
+  }
+  {
+    constexpr monostate a, b;
+    static_assert(!(a < b), "");
+    static_assert(!(a > b), "");
+    static_assert((a <= b), "");
+    static_assert((a == b), "");
+    static_assert(!(a != b), "");
+    static_assert((a >= b), "");
   }
 }
 
@@ -262,14 +288,59 @@ void test_constexpr()
        constexpr literal() = default;
     };
 
-    struct nonliteral {
-       nonliteral() { }
-    };
-
     constexpr variant<literal, nonliteral> v{};
     constexpr variant<literal, nonliteral> v1{in_place_type<literal>};
     constexpr variant<literal, nonliteral> v2{in_place_index<0>};
   }
+
+  {
+    constexpr variant<int> a(42);
+    static_assert(get<0>(a) == 42, "");
+  }
+  {
+    constexpr variant<int, nonliteral> a(42);
+    static_assert(get<0>(a) == 42, "");
+  }
+  {
+    constexpr variant<nonliteral, int> a(42);
+    static_assert(get<1>(a) == 42, "");
+  }
+  {
+    constexpr variant<int> a(42);
+    static_assert(get<int>(a) == 42, "");
+  }
+  {
+    constexpr variant<int, nonliteral> a(42);
+    static_assert(get<int>(a) == 42, "");
+  }
+  {
+    constexpr variant<nonliteral, int> a(42);
+    static_assert(get<int>(a) == 42, "");
+  }
+  {
+    constexpr variant<int> a(42);
+    static_assert(get<0>(std::move(a)) == 42, "");
+  }
+  {
+    constexpr variant<int, nonliteral> a(42);
+    static_assert(get<0>(std::move(a)) == 42, "");
+  }
+  {
+    constexpr variant<nonliteral, int> a(42);
+    static_assert(get<1>(std::move(a)) == 42, "");
+  }
+  {
+    constexpr variant<int> a(42);
+    static_assert(get<int>(std::move(a)) == 42, "");
+  }
+  {
+    constexpr variant<int, nonliteral> a(42);
+    static_assert(get<int>(std::move(a)) == 42, "");
+  }
+  {
+    constexpr variant<nonliteral, int> a(42);
+    static_assert(get<int>(std::move(a)) == 42, "");
+  }
 }
 
 void test_pr77641()