diff mbox

[Patches] Add variant constexpr support for visit, comparisons and get

Message ID CAG4ZjN=0mm5oO3n0gUsnqrveYtGtjGvZFgCcJvNzGs-7nhoA2g@mail.gmail.com
State New
Headers show

Commit Message

Tim Shen Dec. 3, 2016, 3:14 a.m. UTC
On Wed, Nov 30, 2016 at 8:27 AM, Jonathan Wakely wrote:
> On 26/11/16 21:38 -0800, Tim Shen wrote:
>>
>> This 4-patch series contains the following in order:
>>
>> a.diff: Remove uses-allocator ctors. They are going away, and removing
>> it reduces the maintenance burden from now on.
>
>
> Yay! less code.

Yay! Also removed the unused #include.

>
>> -  template<typename _Type, bool __is_literal =
>> std::is_literal_type_v<_Type>>
>> +  // _Uninitialized nullify the destructor calls.
>
>
> This wording confused me slightly. How about:
>
>  "_Uninitialized makes destructors trivial"

Change this section of comment to the discussed content.

>
>> +  // This is necessary, since we define _Variadic_union as a recursive
>> union,
>> +  // and we don't want to inspect the union members one by one in its
>> dtor,
>> +  // it's slow.
>
>
> Please change "it's slow" to "that's slow".

N/A.

>
>> +  template<typename _Type, bool = std::is_literal_type_v<_Type>>
>>     struct _Uninitialized;
>
>
> I'm still unsure that is_literal_type is the right trait here. If it's
> definitely right then we should probably *not* deprecate it in C++17!

Already discussed.

>
>>   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)...); }
>>
>> +      const _Type& _M_get() const &
>> +      {
>> +       return *static_cast<const _Type*>(
>> +           static_cast<const void*>(&_M_storage));
>> +      }
>> +
>> +      _Type& _M_get() &
>> +      { return *static_cast<_Type*>(static_cast<void*>(&_M_storage)); }
>> +
>> +      const _Type&& _M_get() const &&
>> +      {
>> +       return std::move(*static_cast<const _Type*>(
>> +           static_cast<const void*>(&_M_storage)));
>> +      }
>> +
>> +      _Type&& _M_get() &&
>> +      {
>> +       return
>> std::move(*static_cast<_Type*>(static_cast<void*>(&_M_storage)));
>> +      }
>> +
>>       typename std::aligned_storage<sizeof(_Type), alignof(_Type)>::type
>>           _M_storage;
>
>
> I think this could use __aligned_membuf, which would reduce the
> alignment requirements for some types (e.g. long long on x86-32).

Done.

>
> That would also mean you get the _M_ptr() member so don't need all the
> casts.
>
>> +      ~_Variant_storage()
>> +      { _M_destroy_impl(std::make_index_sequence<sizeof...(_Types)>{}); }
>
>
> You can use index_sequence_for<_Types...> here.

Done

>
>> @@ -598,9 +645,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 T>
>
>
> This needs to be _Tp not T

Done.

>
>> +      return __lhs._M_equal_to(__rhs,
>> +
>> std::make_index_sequence<sizeof...(_Types)>{});
>
>
> Another one that could use index_sequence_for<_Types...>

Done.

>
>> +      return __lhs._M_less_than(__rhs,
>> +
>> std::make_index_sequence<sizeof...(_Types)>{});
>
>
> Same again.

Same again. ;)

>
>
>>            * include/bits/enable_special_members.h: Make
>>            _Enable_default_constructor constexpr.
>>            * include/std/variant (variant::emplace, variant::swap,
>> std::swap,
>>            std::hash): Sfinae on emplace and std::swap; handle
>> __poison_hash bases
>>            of duplicated types.
>>
>> diff --git a/libstdc++-v3/include/bits/enable_special_members.h
>> b/libstdc++-v3/include/bits/enable_special_members.h
>> index 07c6c99..4f4477b 100644
>> --- a/libstdc++-v3/include/bits/enable_special_members.h
>> +++ b/libstdc++-v3/include/bits/enable_special_members.h
>> @@ -118,7 +118,8 @@ template<typename _Tag>
>>     operator=(_Enable_default_constructor&&) noexcept = default;
>>
>>     // Can be used in other ctors.
>> -    explicit _Enable_default_constructor(_Enable_default_constructor_tag)
>> { }
>> +    constexpr explicit
>> +    _Enable_default_constructor(_Enable_default_constructor_tag) { }
>>   };
>>
>> +      void _M_reset()
>> +      {
>> +       _M_reset_impl(std::make_index_sequence<sizeof...(_Types)>{});
>> +       _M_index = variant_npos;
>> +      }
>> +
>>       ~_Variant_storage()
>> -      { _M_destroy_impl(std::make_index_sequence<sizeof...(_Types)>{}); }
>> +      { _M_reset(); }
>
>
> These can also use index_sequence_for<_Types...>

Done.

>
>> @@ -1253,14 +1285,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>
>>   template<typename... _Types>
>>     struct hash<variant<_Types...>>
>> -    : private __poison_hash<remove_const_t<_Types>>...
>> +    : private __detail::__variant::_Variant_hash_base<
>> +       variant<_Types...>, std::make_index_sequence<sizeof...(_Types)>>
>
>
> And again.

And again.

>
>>     {
>>       using result_type = size_t;
>>       using argument_type = variant<_Types...>;
>>
>>       size_t
>>       operator()(const variant<_Types...>& __t) const
>> -      noexcept((... &&
>> noexcept(hash<decay_t<_Types>>{}(std::declval<_Types>()))))
>> +      noexcept((noexcept(hash<decay_t<_Types>>{}(std::declval<_Types>()))
>> +               && ...))
>
>
> This could be
> __and_<is_nothrow_callable<hash<decay_t<_Types>>(_Types)>...>
> but I'm not sure it would be an improvement. The is_callable check is
> expensive, but maybe we need it anyway to correctly disable this
> function if the hash specialization should be posisoned?

Done. I just realized that is_nothrow_callable also handles crazy
member pointer cases.

Used fold expression instead of __and_ for consistency.

>
>
>> @@ -1270,17 +1239,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>     }
>>
>>   template<typename _Visitor, typename... _Variants>
>> -    decltype(auto)
>> +    constexpr decltype(auto)
>>     visit(_Visitor&& __visitor, _Variants&&... __variants)
>>     {
>> +      if ((__variants.valueless_by_exception() || ...))
>> +       __throw_bad_variant_access("Unexpected index");
>> +
>>       using _Result_type =
>>
>> decltype(std::forward<_Visitor>(__visitor)(get<0>(__variants)...));
>> -      static constexpr auto _S_vtable =
>> +      constexpr auto _S_vtable =
>
>
> If this isn't static now it could be called simply __vtable, the _S_
> prefix is misleading. How many of these _S_vtable variables actually
> need to be static? If they're all trivial types and constexpr then it
> probably doesn't matter either way, there shouldn't be any difference.

Ah that's an oversight. Moved the static variable out of visit().
_S_vtable needs to be static, otherwise runtime O(n) assignment will
happen, where n is the size of _S_vtable.

Comments

Jonathan Wakely Dec. 6, 2016, 10:30 a.m. UTC | #1
On 02/12/16 19:14 -0800, Tim Shen wrote:
>On Wed, Nov 30, 2016 at 8:27 AM, Jonathan Wakely wrote:
>> On 26/11/16 21:38 -0800, Tim Shen wrote:
>>>
>>> This 4-patch series contains the following in order:
>>>
>>> a.diff: Remove uses-allocator ctors. They are going away, and removing
>>> it reduces the maintenance burden from now on.
>>
>>
>> Yay! less code.
>
>Yay! Also removed the unused #include.
>
>>
>>> -  template<typename _Type, bool __is_literal =
>>> std::is_literal_type_v<_Type>>
>>> +  // _Uninitialized nullify the destructor calls.
>>
>>
>> This wording confused me slightly. How about:
>>
>>  "_Uninitialized makes destructors trivial"
>
>Change this section of comment to the discussed content.
>
>>
>>> +  // This is necessary, since we define _Variadic_union as a recursive
>>> union,
>>> +  // and we don't want to inspect the union members one by one in its
>>> dtor,
>>> +  // it's slow.
>>
>>
>> Please change "it's slow" to "that's slow".
>
>N/A.
>
>>
>>> +  template<typename _Type, bool = std::is_literal_type_v<_Type>>
>>>     struct _Uninitialized;
>>
>>
>> I'm still unsure that is_literal_type is the right trait here. If it's
>> definitely right then we should probably *not* deprecate it in C++17!
>
>Already discussed.
>
>>
>>>   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)...); }
>>>
>>> +      const _Type& _M_get() const &
>>> +      {
>>> +       return *static_cast<const _Type*>(
>>> +           static_cast<const void*>(&_M_storage));
>>> +      }
>>> +
>>> +      _Type& _M_get() &
>>> +      { return *static_cast<_Type*>(static_cast<void*>(&_M_storage)); }
>>> +
>>> +      const _Type&& _M_get() const &&
>>> +      {
>>> +       return std::move(*static_cast<const _Type*>(
>>> +           static_cast<const void*>(&_M_storage)));
>>> +      }
>>> +
>>> +      _Type&& _M_get() &&
>>> +      {
>>> +       return
>>> std::move(*static_cast<_Type*>(static_cast<void*>(&_M_storage)));
>>> +      }
>>> +
>>>       typename std::aligned_storage<sizeof(_Type), alignof(_Type)>::type
>>>           _M_storage;
>>
>>
>> I think this could use __aligned_membuf, which would reduce the
>> alignment requirements for some types (e.g. long long on x86-32).
>
>Done.
>
>>
>> That would also mean you get the _M_ptr() member so don't need all the
>> casts.
>>
>>> +      ~_Variant_storage()
>>> +      { _M_destroy_impl(std::make_index_sequence<sizeof...(_Types)>{}); }
>>
>>
>> You can use index_sequence_for<_Types...> here.
>
>Done
>
>>
>>> @@ -598,9 +645,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 T>
>>
>>
>> This needs to be _Tp not T
>
>Done.
>
>>
>>> +      return __lhs._M_equal_to(__rhs,
>>> +
>>> std::make_index_sequence<sizeof...(_Types)>{});
>>
>>
>> Another one that could use index_sequence_for<_Types...>
>
>Done.
>
>>
>>> +      return __lhs._M_less_than(__rhs,
>>> +
>>> std::make_index_sequence<sizeof...(_Types)>{});
>>
>>
>> Same again.
>
>Same again. ;)
>
>>
>>
>>>            * include/bits/enable_special_members.h: Make
>>>            _Enable_default_constructor constexpr.
>>>            * include/std/variant (variant::emplace, variant::swap,
>>> std::swap,
>>>            std::hash): Sfinae on emplace and std::swap; handle
>>> __poison_hash bases
>>>            of duplicated types.
>>>
>>> diff --git a/libstdc++-v3/include/bits/enable_special_members.h
>>> b/libstdc++-v3/include/bits/enable_special_members.h
>>> index 07c6c99..4f4477b 100644
>>> --- a/libstdc++-v3/include/bits/enable_special_members.h
>>> +++ b/libstdc++-v3/include/bits/enable_special_members.h
>>> @@ -118,7 +118,8 @@ template<typename _Tag>
>>>     operator=(_Enable_default_constructor&&) noexcept = default;
>>>
>>>     // Can be used in other ctors.
>>> -    explicit _Enable_default_constructor(_Enable_default_constructor_tag)
>>> { }
>>> +    constexpr explicit
>>> +    _Enable_default_constructor(_Enable_default_constructor_tag) { }
>>>   };
>>>
>>> +      void _M_reset()
>>> +      {
>>> +       _M_reset_impl(std::make_index_sequence<sizeof...(_Types)>{});
>>> +       _M_index = variant_npos;
>>> +      }
>>> +
>>>       ~_Variant_storage()
>>> -      { _M_destroy_impl(std::make_index_sequence<sizeof...(_Types)>{}); }
>>> +      { _M_reset(); }
>>
>>
>> These can also use index_sequence_for<_Types...>
>
>Done.
>
>>
>>> @@ -1253,14 +1285,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>
>>>   template<typename... _Types>
>>>     struct hash<variant<_Types...>>
>>> -    : private __poison_hash<remove_const_t<_Types>>...
>>> +    : private __detail::__variant::_Variant_hash_base<
>>> +       variant<_Types...>, std::make_index_sequence<sizeof...(_Types)>>
>>
>>
>> And again.
>
>And again.
>
>>
>>>     {
>>>       using result_type = size_t;
>>>       using argument_type = variant<_Types...>;
>>>
>>>       size_t
>>>       operator()(const variant<_Types...>& __t) const
>>> -      noexcept((... &&
>>> noexcept(hash<decay_t<_Types>>{}(std::declval<_Types>()))))
>>> +      noexcept((noexcept(hash<decay_t<_Types>>{}(std::declval<_Types>()))
>>> +               && ...))
>>
>>
>> This could be
>> __and_<is_nothrow_callable<hash<decay_t<_Types>>(_Types)>...>
>> but I'm not sure it would be an improvement. The is_callable check is
>> expensive, but maybe we need it anyway to correctly disable this
>> function if the hash specialization should be posisoned?
>
>Done. I just realized that is_nothrow_callable also handles crazy
>member pointer cases.
>
>Used fold expression instead of __and_ for consistency.
>
>>
>>
>>> @@ -1270,17 +1239,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>     }
>>>
>>>   template<typename _Visitor, typename... _Variants>
>>> -    decltype(auto)
>>> +    constexpr decltype(auto)
>>>     visit(_Visitor&& __visitor, _Variants&&... __variants)
>>>     {
>>> +      if ((__variants.valueless_by_exception() || ...))
>>> +       __throw_bad_variant_access("Unexpected index");
>>> +
>>>       using _Result_type =
>>>
>>> decltype(std::forward<_Visitor>(__visitor)(get<0>(__variants)...));
>>> -      static constexpr auto _S_vtable =
>>> +      constexpr auto _S_vtable =
>>
>>
>> If this isn't static now it could be called simply __vtable, the _S_
>> prefix is misleading. How many of these _S_vtable variables actually
>> need to be static? If they're all trivial types and constexpr then it
>> probably doesn't matter either way, there shouldn't be any difference.
>
>Ah that's an oversight. Moved the static variable out of visit().
>_S_vtable needs to be static, otherwise runtime O(n) assignment will
>happen, where n is the size of _S_vtable.

This looks good - OK for trunk, thanks!
Tim Shen Dec. 6, 2016, 11:52 a.m. UTC | #2
On Tue, Dec 6, 2016 at 2:30 AM, Jonathan Wakely wrote:
> This looks good - OK for trunk, thanks!

Committed.

Thanks!
diff mbox

Patch

commit 7f64577f6f54e820f7a313085d09c5f64caa6c1e
Author: Tim Shen <timshen@google.com>
Date:   Sat Nov 26 20:10:40 2016 -0800

    2016-11-27  Tim Shen  <timshen@google.com>
    
            * include/std/variant (visit): Make visit constexpr. Also cleanup
            __get_alternative and __storage, since we don't support reference/void
            alternatives any more.
            * testsuite/20_util/variant/compile.cc: Add tests.

diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant
index fa1e654..dd6109d 100644
--- a/libstdc++-v3/include/std/variant
+++ b/libstdc++-v3/include/std/variant
@@ -41,11 +41,34 @@ 
 #include <bits/functexcept.h>
 #include <bits/move.h>
 #include <bits/functional_hash.h>
+#include <bits/invoke.h>
 #include <ext/aligned_buffer.h>
 
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
+namespace __detail
+{
+namespace __variant
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  template<size_t _Np, typename... _Types>
+    struct _Nth_type;
+
+  template<size_t _Np, typename _First, typename... _Rest>
+    struct _Nth_type<_Np, _First, _Rest...>
+    : _Nth_type<_Np-1, _Rest...> { };
+
+  template<typename _First, typename... _Rest>
+    struct _Nth_type<0, _First, _Rest...>
+    { using type = _First; };
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace __variant
+} // namespace __detail
+
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename... _Types> class tuple;
   template<typename... _Types> class variant;
@@ -99,6 +122,22 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   constexpr size_t variant_npos = -1;
 
+  template<size_t _Np, typename... _Types>
+    constexpr variant_alternative_t<_Np, variant<_Types...>>&
+    get(variant<_Types...>&);
+
+  template<size_t _Np, typename... _Types>
+    constexpr variant_alternative_t<_Np, variant<_Types...>>&&
+    get(variant<_Types...>&&);
+
+  template<size_t _Np, typename... _Types>
+    constexpr variant_alternative_t<_Np, variant<_Types...>> const&
+    get(const variant<_Types...>&);
+
+  template<size_t _Np, typename... _Types>
+    constexpr variant_alternative_t<_Np, variant<_Types...>> const&&
+    get(const variant<_Types...>&&);
+
 _GLIBCXX_END_NAMESPACE_VERSION
 
 namespace __detail
@@ -119,41 +158,6 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       std::integral_constant<size_t, is_same_v<_Tp, _First>
 	? 0 : __index_of_v<_Tp, _Rest...> + 1> {};
 
-  // Extract _From's qualifiers and references and apply it to _To.
-  // __reserved_type_map<const int&, char> is const char&.
-  template<typename _From, typename _To>
-    struct __reserved_type_map_impl
-    { using type = _To; };
-
-  template<typename _From, typename _To>
-    using __reserved_type_map =
-      typename __reserved_type_map_impl<_From, _To>::type;
-
-  template<typename _From, typename _To>
-    struct __reserved_type_map_impl<_From&, _To>
-    { using type = add_lvalue_reference_t<__reserved_type_map<_From, _To>>; };
-
-  template<typename _From, typename _To>
-    struct __reserved_type_map_impl<_From&&, _To>
-    { using type = add_rvalue_reference_t<__reserved_type_map<_From, _To>>; };
-
-  template<typename _From, typename _To>
-    struct __reserved_type_map_impl<const _From, _To>
-    { using type = add_const_t<__reserved_type_map<_From, _To>>; };
-
-  template<typename _From, typename _To>
-    struct __reserved_type_map_impl<volatile _From, _To>
-    { using type = add_volatile_t<__reserved_type_map<_From, _To>>; };
-
-  template<typename _From, typename _To>
-    struct __reserved_type_map_impl<const volatile _From, _To>
-    { using type = add_cv_t<__reserved_type_map<_From, _To>>; };
-
-  // This abstraction might be useful for future features,
-  // e.g. boost::recursive_wrapper.
-  template<typename _Alternative>
-    using __storage = _Alternative;
-
   // _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
@@ -210,16 +214,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       __gnu_cxx::__aligned_membuf<_Type> _M_storage;
     };
 
-  // Given a qualified storage type, return the desired reference.
-  // For example, variant<int>&& stores the int as __storage<int>, and
-  // _Qualified_storage will be __storage<int>&&.
-  template<typename _Qualified_storage>
-    decltype(auto)
-    __get_alternative(void* __ptr)
+  template<typename _Ref>
+    _Ref __ref_cast(void* __ptr)
     {
-      using _Storage = decay_t<_Qualified_storage>;
-      return __reserved_type_map<_Qualified_storage, _Storage>(
-	*static_cast<_Storage*>(__ptr));
+      return static_cast<_Ref>(*static_cast<remove_reference_t<_Ref>*>(__ptr));
     }
 
   template<typename _Union>
@@ -242,7 +240,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Lhs, typename _Rhs>
     constexpr void
     __erased_ctor(void* __lhs, void* __rhs)
-    { ::new (__lhs) decay_t<_Lhs>(__get_alternative<_Rhs>(__rhs)); }
+    { ::new (__lhs) remove_reference_t<_Lhs>(__ref_cast<_Rhs>(__rhs)); }
 
   template<typename _Variant, size_t _Np>
     constexpr void
@@ -256,14 +254,14 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Lhs, typename _Rhs>
     constexpr void
     __erased_assign(void* __lhs, void* __rhs)
-    { __get_alternative<_Lhs>(__lhs) = __get_alternative<_Rhs>(__rhs); }
+    { __ref_cast<_Lhs>(__lhs) = __ref_cast<_Rhs>(__rhs); }
 
   template<typename _Lhs, typename _Rhs>
     constexpr void
     __erased_swap(void* __lhs, void* __rhs)
     {
       using std::swap;
-      swap(__get_alternative<_Lhs>(__lhs), __get_alternative<_Rhs>(__rhs));
+      swap(__ref_cast<_Lhs>(__lhs), __ref_cast<_Rhs>(__rhs));
     }
 
   template<typename _Variant, size_t _Np>
@@ -285,7 +283,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     constexpr size_t
     __erased_hash(void* __t)
-    { return std::hash<decay_t<_Tp>>{}(__get_alternative<_Tp>(__t)); }
+    {
+      return std::hash<remove_cv_t<remove_reference_t<_Tp>>>{}(
+	  __ref_cast<_Tp>(__t));
+    }
 
   // Defines members and ctors.
   template<typename... _Types>
@@ -389,8 +390,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	if (__rhs._M_valid())
 	  {
 	    static constexpr void (*_S_vtable[])(void*, void*) =
-	      { &__erased_ctor<__storage<_Types>&,
-			       const __storage<_Types>&>... };
+	      { &__erased_ctor<_Types&, const _Types&>... };
 	    _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage());
 	    this->_M_index = __rhs._M_index;
 	  }
@@ -402,7 +402,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	if (__rhs._M_valid())
 	  {
 	    static constexpr void (*_S_vtable[])(void*, void*) =
-	      { &__erased_ctor<__storage<_Types>&, __storage<_Types>&&>... };
+	      { &__erased_ctor<_Types&, _Types&&>... };
 	    _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage());
 	    this->_M_index = __rhs._M_index;
 	  }
@@ -422,8 +422,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    if (__rhs._M_valid())
 	      {
 		static constexpr void (*_S_vtable[])(void*, void*) =
-		  { &__erased_assign<__storage<_Types>&,
-				     const __storage<_Types>&>... };
+		  { &__erased_assign<_Types&, const _Types&>... };
 		_S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage());
 	      }
 	  }
@@ -469,8 +468,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    if (__rhs._M_valid())
 	      {
 		static constexpr void (*_S_vtable[])(void*, void*) =
-		  { &__erased_assign<__storage<_Types>&,
-				     __storage<_Types>&&>... };
+		  { &__erased_assign<_Types&, _Types&&>... };
 		_S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage());
 	      }
 	  }
@@ -555,20 +553,6 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     void* __get_storage(_Variant&& __v)
     { return __v._M_storage(); }
 
-  // A helper used to create variadic number of _To types.
-  template<typename _From, typename _To>
-    using _To_type = _To;
-
-  // Call the actual visitor.
-  // _Args are qualified storage types.
-  template<typename _Visitor, typename... _Args>
-    decltype(auto)
-    __visit_invoke(_Visitor&& __visitor, _To_type<_Args, void*>... __ptrs)
-    {
-      return std::forward<_Visitor>(__visitor)(
-	  __get_alternative<_Args>(__ptrs)...);
-    }
-
   // Used for storing multi-dimensional vtable.
   template<typename _Tp, size_t... _Dimensions>
     struct _Multi_array
@@ -592,108 +576,115 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   // Creates a multi-dimensional vtable recursively.
-  // _Variant_tuple is initially the input from visit(), and gets gradually
-  // consumed.
-  // _Arg_tuple is enumerated alternative sequence, represented by a
-  // qualified storage.
   //
   // For example,
   // visit([](auto, auto){},
-  //       variant<int, char>(),
-  //       variant<float, double, long double>())
+  //       variant<int, char>(),  // typedef'ed as V1
+  //       variant<float, double, long double>())  // typedef'ed as V2
   // will trigger instantiations of:
-  // __gen_vtable_impl<_Multi_array<void(*)(void*, void*), 2, 3>,
-  //                   tuple<variant<int, char>,
-  //                         variant<float, double, long double>>,
-  //                   tuple<>>
-  //   __gen_vtable_impl<_Multi_array<void(*)(void*, void*), 3>,
-  //                     tuple<variant<float, double, long double>>,
-  //                     tuple<int>>
-  //     __gen_vtable_impl<_Multi_array<void(*)(void*, void*)>,
-  //                       tuple<>,
-  //                       tuple<int, float>>
-  //     __gen_vtable_impl<_Multi_array<void(*)(void*, void*)>,
-  //                       tuple<>,
-  //                       tuple<int, double>>
-  //     __gen_vtable_impl<_Multi_array<void(*)(void*, void*)>,
-  //                       tuple<>,
-  //                       tuple<int, long double>>
-  //   __gen_vtable_impl<_Multi_array<void(*)(void*, void*), 3>,
-  //                     tuple<variant<float, double, long double>>,
-  //                     tuple<char>>
-  //     __gen_vtable_impl<_Multi_array<void(*)(void*, void*)>,
-  //                       tuple<>,
-  //                       tuple<char, float>>
-  //     __gen_vtable_impl<_Multi_array<void(*)(void*, void*)>,
-  //                       tuple<>,
-  //                       tuple<char, double>>
-  //     __gen_vtable_impl<_Multi_array<void(*)(void*, void*)>,
-  //                       tuple<>,
-  //                       tuple<char, long double>>
+  // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&), 2, 3>,
+  //                   tuple<V1&&, V2&&>, std::index_sequence<>>
+  //   __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&), 3>,
+  //                     tuple<V1&&, V2&&>, std::index_sequence<0>>
+  //     __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>,
+  //                       tuple<V1&&, V2&&>, std::index_sequence<0, 0>>
+  //     __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>,
+  //                       tuple<V1&&, V2&&>, std::index_sequence<0, 1>>
+  //     __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>,
+  //                       tuple<V1&&, V2&&>, std::index_sequence<0, 2>>
+  //   __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&), 3>,
+  //                     tuple<V1&&, V2&&>, std::index_sequence<1>>
+  //     __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>,
+  //                       tuple<V1&&, V2&&>, std::index_sequence<1, 0>>
+  //     __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>,
+  //                       tuple<V1&&, V2&&>, std::index_sequence<1, 1>>
+  //     __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>,
+  //                       tuple<V1&&, V2&&>, std::index_sequence<1, 2>>
   // The returned multi-dimensional vtable can be fast accessed by the visitor
   // using index calculation.
-  template<typename _Array_type, typename _Variant_tuple, typename _Arg_tuple>
+  template<typename _Array_type, typename _Variant_tuple, typename _Index_seq>
     struct __gen_vtable_impl;
 
-  template<typename _Array_type, typename _First, typename... _Rest,
-	   typename... _Args>
-    struct __gen_vtable_impl<_Array_type, tuple<_First, _Rest...>,
-			     tuple<_Args...>>
+  template<typename _Result_type, typename _Visitor, size_t... __unused,
+	   typename... _Variants, size_t... __indices>
+    struct __gen_vtable_impl<
+	_Multi_array<_Result_type (*)(_Visitor, _Variants...), __unused...>,
+	tuple<_Variants...>, std::index_sequence<__indices...>>
     {
+      using _Next =
+	  remove_reference_t<typename _Nth_type<sizeof...(__indices),
+			     _Variants...>::type>;
+      using _Array_type =
+	  _Multi_array<_Result_type (*)(_Visitor, _Variants...), __unused...>;
+
       static constexpr _Array_type
       _S_apply()
       {
 	_Array_type __vtable{};
 	_S_apply_all_alts(
-	  __vtable, make_index_sequence<variant_size_v<decay_t<_First>>>());
+	  __vtable, make_index_sequence<variant_size_v<_Next>>());
 	return __vtable;
       }
 
-      template<size_t... __indices>
+      template<size_t... __var_indices>
 	static constexpr void
-	_S_apply_all_alts(_Array_type& __vtable, index_sequence<__indices...>)
-	{ (_S_apply_single_alt<__indices>(__vtable._M_arr[__indices]), ...); }
+	_S_apply_all_alts(_Array_type& __vtable,
+			  std::index_sequence<__var_indices...>)
+	{
+	  (_S_apply_single_alt<__var_indices>(
+	     __vtable._M_arr[__var_indices]), ...);
+	}
 
       template<size_t __index, typename _Tp>
 	static constexpr void
 	_S_apply_single_alt(_Tp& __element)
 	{
-	  using _Alternative = variant_alternative_t<__index, decay_t<_First>>;
-	  using _Qualified_storage = __reserved_type_map<
-	    _First, __storage<_Alternative>>;
+	  using _Alternative = variant_alternative_t<__index, _Next>;
 	  __element = __gen_vtable_impl<
-	    decay_t<decltype(__element)>, tuple<_Rest...>,
-	    tuple<_Args..., _Qualified_storage>>::_S_apply();
+	    remove_reference_t<
+	      decltype(__element)>, tuple<_Variants...>,
+	      std::index_sequence<__indices..., __index>>::_S_apply();
 	}
     };
 
-  template<typename _Result_type, typename _Visitor, typename... _Args>
+  template<typename _Result_type, typename _Visitor, typename... _Variants,
+	   size_t... __indices>
     struct __gen_vtable_impl<
-      _Multi_array<_Result_type (*)(_Visitor, _To_type<_Args, void*>...)>,
-		   tuple<>, tuple<_Args...>>
+      _Multi_array<_Result_type (*)(_Visitor, _Variants...)>,
+		   tuple<_Variants...>, std::index_sequence<__indices...>>
     {
       using _Array_type =
-	_Multi_array<_Result_type (*)(_Visitor&&, _To_type<_Args, void*>...)>;
+	  _Multi_array<_Result_type (*)(_Visitor&&, _Variants...)>;
+
+      decltype(auto)
+      static constexpr __visit_invoke(_Visitor&& __visitor, _Variants... __vars)
+      {
+	return __invoke(std::forward<_Visitor>(__visitor),
+			std::get<__indices>(
+			    std::forward<_Variants>(__vars))...);
+      }
 
       static constexpr auto
       _S_apply()
-      { return _Array_type{&__visit_invoke<_Visitor, _Args...>}; }
+      { return _Array_type{&__visit_invoke}; }
     };
 
   template<typename _Result_type, typename _Visitor, typename... _Variants>
     struct __gen_vtable
     {
-      using _Func_ptr =
-	_Result_type (*)(_Visitor&&, _To_type<_Variants, void*>...);
+      using _Func_ptr = _Result_type (*)(_Visitor&&, _Variants...);
       using _Array_type =
-	_Multi_array<_Func_ptr, variant_size_v<decay_t<_Variants>>...>;
+	  _Multi_array<_Func_ptr,
+		       variant_size_v<remove_reference_t<_Variants>>...>;
 
       static constexpr _Array_type
       _S_apply()
       {
-	return __gen_vtable_impl<
-	  _Array_type, tuple<_Variants...>, tuple<>>::_S_apply();
+	return __gen_vtable_impl<_Array_type, tuple<_Variants...>,
+				 std::index_sequence<>>::_S_apply();
       }
+
+      static constexpr auto _S_vtable = _S_apply();
     };
 
   template<size_t _Np, typename _Tp>
@@ -722,22 +713,6 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       return __v.index() == __detail::__variant::__index_of_v<_Tp, _Types...>;
     }
 
-  template<size_t _Np, typename... _Types>
-    constexpr variant_alternative_t<_Np, variant<_Types...>>&
-    get(variant<_Types...>&);
-
-  template<size_t _Np, typename... _Types>
-    constexpr variant_alternative_t<_Np, variant<_Types...>>&&
-    get(variant<_Types...>&&);
-
-  template<size_t _Np, typename... _Types>
-    constexpr variant_alternative_t<_Np, variant<_Types...>> const&
-    get(const variant<_Types...>&);
-
-  template<size_t _Np, typename... _Types>
-    constexpr variant_alternative_t<_Np, variant<_Types...>> const&&
-    get(const variant<_Types...>&&);
-
   template<typename _Tp, typename... _Types>
     constexpr inline _Tp& get(variant<_Types...>& __v)
     {
@@ -860,7 +835,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return !(__lhs < __rhs); }
 
   template<typename _Visitor, typename... _Variants>
-    decltype(auto) visit(_Visitor&&, _Variants&&...);
+    constexpr decltype(auto) visit(_Visitor&&, _Variants&&...);
 
   struct monostate { };
 
@@ -965,9 +940,6 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	using __accepted_type = __to_type<__accepted_index<_Tp>>;
 
       template<typename _Tp>
-	using __storage = __detail::__variant::__storage<_Tp>;
-
-      template<typename _Tp>
 	static constexpr size_t __index_of =
 	  __detail::__variant::__index_of_v<_Tp, _Types...>;
 
@@ -1127,8 +1099,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    if (this->_M_valid())
 	      {
 		static constexpr void (*_S_vtable[])(void*, void*) =
-		  { &__detail::__variant::__erased_swap<
-		      __storage<_Types>&, __storage<_Types>&>... };
+		  { &__detail::__variant::__erased_swap<_Types&, _Types&>... };
 		_S_vtable[__rhs._M_index](this->_M_storage(),
 					  __rhs._M_storage());
 	      }
@@ -1268,17 +1239,21 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     }
 
   template<typename _Visitor, typename... _Variants>
-    decltype(auto)
+    constexpr decltype(auto)
     visit(_Visitor&& __visitor, _Variants&&... __variants)
     {
+      if ((__variants.valueless_by_exception() || ...))
+	__throw_bad_variant_access("Unexpected index");
+
       using _Result_type =
 	decltype(std::forward<_Visitor>(__visitor)(get<0>(__variants)...));
-      static constexpr auto _S_vtable =
-	__detail::__variant::__gen_vtable<
-	  _Result_type, _Visitor&&, _Variants&&...>::_S_apply();
-      auto __func_ptr = _S_vtable._M_access(__variants.index()...);
+
+      constexpr auto& __vtable = __detail::__variant::__gen_vtable<
+	_Result_type, _Visitor&&, _Variants&&...>::_S_vtable;
+
+      auto __func_ptr = __vtable._M_access(__variants.index()...);
       return (*__func_ptr)(std::forward<_Visitor>(__visitor),
-			   __detail::__variant::__get_storage(__variants)...);
+			   std::forward<_Variants>(__variants)...);
     }
 
   template<typename... _Types>
@@ -1297,7 +1272,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  {
 	    namespace __edv = __detail::__variant;
 	    static constexpr size_t (*_S_vtable[])(void*) =
-	      { &__edv::__erased_hash<const __edv::__storage<_Types>&>... };
+	      { &__edv::__erased_hash<const _Types&>... };
 	    return hash<size_t>{}(__t.index())
 	      + _S_vtable[__t.index()](__edv::__get_storage(__t));
 	  }
diff --git a/libstdc++-v3/testsuite/20_util/variant/compile.cc b/libstdc++-v3/testsuite/20_util/variant/compile.cc
index 087a17c..a8ffaea 100644
--- a/libstdc++-v3/testsuite/20_util/variant/compile.cc
+++ b/libstdc++-v3/testsuite/20_util/variant/compile.cc
@@ -275,6 +275,22 @@  void test_visit()
     };
     visit(Visitor(), variant<int, char>(), variant<float, double>());
   }
+  {
+    struct Visitor
+    {
+      constexpr bool operator()(const int&) { return true; }
+      constexpr bool operator()(const nonliteral&) { return false; }
+    };
+    static_assert(visit(Visitor(), variant<int, nonliteral>(0)), "");
+  }
+  {
+    struct Visitor
+    {
+      constexpr bool operator()(const int&) { return true; }
+      constexpr bool operator()(const nonliteral&) { return false; }
+    };
+    static_assert(visit(Visitor(), variant<int, nonliteral>(0)), "");
+  }
 }
 
 void test_constexpr()