re PR libstdc++/80187 (C++ variant should be trivially copy constructible if possible)
authorTim Shen <timshen@google.com>
Tue, 27 Jun 2017 18:19:03 +0000 (18:19 +0000)
committerTim Shen <timshen@gcc.gnu.org>
Tue, 27 Jun 2017 18:19:03 +0000 (18:19 +0000)
PR libstdc++/80187
* include/std/variant (variant::variant, variant::~variant,
variant::operator=): Implement triviality forwarding for four
special member functions.
* testsuite/20_util/variant/compile.cc: Tests.

From-SVN: r249706

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

index 1d31df9dce4d354d74225951fbd8404fc495bd17..770b5789da7355e353162167d646048059021b99 100644 (file)
@@ -1,3 +1,11 @@
+2017-06-27  Tim Shen  <timshen@google.com>
+
+       PR libstdc++/80187
+       * include/std/variant (variant::variant, variant::~variant,
+       variant::operator=): Implement triviality forwarding for four
+       special member functions.
+       * testsuite/20_util/variant/compile.cc: Tests.
+
 2017-06-27  Jonathan Wakely  <jwakely@redhat.com>
 
        PR libstdc++/81221
index e5fe9f927c1621e3959464e8277489b756d02547..06646f6b7986fb2b36c1654f81b470d6543cd48d 100644 (file)
@@ -295,6 +295,46 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          __variant::__ref_cast<_Tp>(__t));
     }
 
+  template<typename... _Types>
+    struct _Traits
+    {
+      static constexpr bool _S_default_ctor =
+          is_default_constructible_v<typename _Nth_type<0, _Types...>::type>;
+      static constexpr bool _S_copy_ctor =
+          (is_copy_constructible_v<_Types> && ...);
+      static constexpr bool _S_move_ctor =
+          (is_move_constructible_v<_Types> && ...);
+      static constexpr bool _S_copy_assign =
+          _S_copy_ctor && _S_move_ctor
+          && (is_copy_assignable_v<_Types> && ...);
+      static constexpr bool _S_move_assign =
+          _S_move_ctor
+          && (is_move_assignable_v<_Types> && ...);
+
+      static constexpr bool _S_trivial_dtor =
+          (is_trivially_destructible_v<_Types> && ...);
+      static constexpr bool _S_trivial_copy_ctor =
+          (is_trivially_copy_constructible_v<_Types> && ...);
+      static constexpr bool _S_trivial_move_ctor =
+          (is_trivially_move_constructible_v<_Types> && ...);
+      static constexpr bool _S_trivial_copy_assign =
+          _S_trivial_dtor && (is_trivially_copy_assignable_v<_Types> && ...);
+      static constexpr bool _S_trivial_move_assign =
+          _S_trivial_dtor && (is_trivially_move_assignable_v<_Types> && ...);
+
+      // The following nothrow traits are for non-trivial SMFs. Trivial SMFs
+      // are always nothrow.
+      static constexpr bool _S_nothrow_default_ctor =
+          is_nothrow_default_constructible_v<
+              typename _Nth_type<0, _Types...>::type>;
+      static constexpr bool _S_nothrow_copy_ctor = false;
+      static constexpr bool _S_nothrow_move_ctor =
+          (is_nothrow_move_constructible_v<_Types> && ...);
+      static constexpr bool _S_nothrow_copy_assign = false;
+      static constexpr bool _S_nothrow_move_assign =
+          _S_nothrow_move_ctor && (is_nothrow_move_assignable_v<_Types> && ...);
+    };
+
   // Defines members and ctors.
   template<typename... _Types>
     union _Variadic_union { };
@@ -360,6 +400,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       ~_Variant_storage()
       { _M_reset(); }
 
+      void*
+      _M_storage() const
+      {
+       return const_cast<void*>(static_cast<const void*>(
+           std::addressof(_M_u)));
+      }
+
+      constexpr bool
+      _M_valid() const noexcept
+      {
+       return this->_M_index != __index_type(variant_npos);
+      }
+
       _Variadic_union<_Types...> _M_u;
       using __index_type = __select_index<_Types...>;
       __index_type _M_index;
@@ -379,59 +432,108 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       void _M_reset()
       { _M_index = variant_npos; }
 
+      void*
+      _M_storage() const
+      {
+       return const_cast<void*>(static_cast<const void*>(
+           std::addressof(_M_u)));
+      }
+
+      constexpr bool
+      _M_valid() const noexcept
+      {
+       return this->_M_index != __index_type(variant_npos);
+      }
+
       _Variadic_union<_Types...> _M_u;
       using __index_type = __select_index<_Types...>;
       __index_type _M_index;
     };
 
-  // Helps SFINAE on special member functions. Otherwise it can live in variant
-  // class.
   template<typename... _Types>
-    struct _Variant_base :
-      _Variant_storage<(std::is_trivially_destructible_v<_Types> && ...),
-                       _Types...>
-    {
-      using _Storage =
-         _Variant_storage<(std::is_trivially_destructible_v<_Types> && ...),
-                           _Types...>;
+    using _Variant_storage_alias =
+        _Variant_storage<_Traits<_Types...>::_S_trivial_dtor, _Types...>;
 
-      constexpr
-      _Variant_base()
-      noexcept(is_nothrow_default_constructible_v<
-                variant_alternative_t<0, variant<_Types...>>>)
-      : _Variant_base(in_place_index<0>) { }
+  // The following are (Copy|Move) (ctor|assign) layers for forwarding
+  // triviality and handling non-trivial SMF behaviors.
+
+  template<bool, typename... _Types>
+    struct _Copy_ctor_base : _Variant_storage_alias<_Types...>
+    {
+      using _Base = _Variant_storage_alias<_Types...>;
+      using _Base::_Base;
 
-      _Variant_base(const _Variant_base& __rhs)
+      _Copy_ctor_base(const _Copy_ctor_base& __rhs)
+          noexcept(_Traits<_Types...>::_S_nothrow_copy_ctor)
       {
        if (__rhs._M_valid())
          {
            static constexpr void (*_S_vtable[])(void*, void*) =
              { &__erased_ctor<_Types&, const _Types&>... };
-           _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage());
+           _S_vtable[__rhs._M_index](this->_M_storage(), __rhs._M_storage());
            this->_M_index = __rhs._M_index;
          }
       }
 
-      _Variant_base(_Variant_base&& __rhs)
-      noexcept((is_nothrow_move_constructible_v<_Types> && ...))
+      _Copy_ctor_base(_Copy_ctor_base&&) = default;
+      _Copy_ctor_base& operator=(const _Copy_ctor_base&) = default;
+      _Copy_ctor_base& operator=(_Copy_ctor_base&&) = default;
+    };
+
+  template<typename... _Types>
+    struct _Copy_ctor_base<true, _Types...> : _Variant_storage_alias<_Types...>
+    {
+      using _Base = _Variant_storage_alias<_Types...>;
+      using _Base::_Base;
+    };
+
+  template<typename... _Types>
+    using _Copy_ctor_alias =
+        _Copy_ctor_base<_Traits<_Types...>::_S_trivial_copy_ctor, _Types...>;
+
+  template<bool, typename... _Types>
+    struct _Move_ctor_base : _Copy_ctor_alias<_Types...>
+    {
+      using _Base = _Copy_ctor_alias<_Types...>;
+      using _Base::_Base;
+
+      _Move_ctor_base(_Move_ctor_base&& __rhs)
+          noexcept(_Traits<_Types...>::_S_nothrow_move_ctor)
       {
        if (__rhs._M_valid())
          {
            static constexpr void (*_S_vtable[])(void*, void*) =
              { &__erased_ctor<_Types&, _Types&&>... };
-           _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage());
+           _S_vtable[__rhs._M_index](this->_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)...)
-       { }
+      _Move_ctor_base(const _Move_ctor_base&) = default;
+      _Move_ctor_base& operator=(const _Move_ctor_base&) = default;
+      _Move_ctor_base& operator=(_Move_ctor_base&&) = default;
+    };
+
+  template<typename... _Types>
+    struct _Move_ctor_base<true, _Types...> : _Copy_ctor_alias<_Types...>
+    {
+      using _Base = _Copy_ctor_alias<_Types...>;
+      using _Base::_Base;
+    };
+
+  template<typename... _Types>
+    using _Move_ctor_alias =
+        _Move_ctor_base<_Traits<_Types...>::_S_trivial_move_ctor, _Types...>;
+
+  template<bool, typename... _Types>
+    struct _Copy_assign_base : _Move_ctor_alias<_Types...>
+    {
+      using _Base = _Move_ctor_alias<_Types...>;
+      using _Base::_Base;
 
-      _Variant_base&
-      operator=(const _Variant_base& __rhs)
+      _Copy_assign_base&
+      operator=(const _Copy_assign_base& __rhs)
+          noexcept(_Traits<_Types...>::_S_nothrow_copy_assign)
       {
        if (this->_M_index == __rhs._M_index)
          {
@@ -439,16 +541,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
              {
                static constexpr void (*_S_vtable[])(void*, void*) =
                  { &__erased_assign<_Types&, const _Types&>... };
-               _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage());
+               _S_vtable[__rhs._M_index](this->_M_storage(), __rhs._M_storage());
              }
          }
        else
          {
-           _Variant_base __tmp(__rhs);
-           this->~_Variant_base();
+           _Copy_assign_base __tmp(__rhs);
+           this->~_Copy_assign_base();
            __try
              {
-               ::new (this) _Variant_base(std::move(__tmp));
+               ::new (this) _Copy_assign_base(std::move(__tmp));
              }
            __catch (...)
              {
@@ -460,12 +562,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        return *this;
       }
 
-      void _M_destructive_move(_Variant_base&& __rhs)
+      _Copy_assign_base(const _Copy_assign_base&) = default;
+      _Copy_assign_base(_Copy_assign_base&&) = default;
+      _Copy_assign_base& operator=(_Copy_assign_base&&) = default;
+    };
+
+  template<typename... _Types>
+    struct _Copy_assign_base<true, _Types...> : _Move_ctor_alias<_Types...>
+    {
+      using _Base = _Move_ctor_alias<_Types...>;
+      using _Base::_Base;
+    };
+
+  template<typename... _Types>
+    using _Copy_assign_alias =
+        _Copy_assign_base<_Traits<_Types...>::_S_trivial_copy_assign,
+                          _Types...>;
+
+  template<bool, typename... _Types>
+    struct _Move_assign_base : _Copy_assign_alias<_Types...>
+    {
+      using _Base = _Copy_assign_alias<_Types...>;
+      using _Base::_Base;
+
+      void _M_destructive_move(_Move_assign_base&& __rhs)
       {
-       this->~_Variant_base();
+       this->~_Move_assign_base();
        __try
          {
-           ::new (this) _Variant_base(std::move(__rhs));
+           ::new (this) _Move_assign_base(std::move(__rhs));
          }
        __catch (...)
          {
@@ -474,40 +599,74 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          }
       }
 
-      _Variant_base&
-      operator=(_Variant_base&& __rhs)
-      noexcept((is_nothrow_move_constructible_v<_Types> && ...)
-         && (is_nothrow_move_assignable_v<_Types> && ...))
+      _Move_assign_base&
+      operator=(_Move_assign_base&& __rhs)
+          noexcept(_Traits<_Types...>::_S_nothrow_move_assign)
       {
        if (this->_M_index == __rhs._M_index)
          {
            if (__rhs._M_valid())
              {
                static constexpr void (*_S_vtable[])(void*, void*) =
-                 { &__erased_assign<_Types&, _Types&&>... };
-               _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage());
+                 { &__erased_assign<_Types&, const _Types&>... };
+               _S_vtable[__rhs._M_index](this->_M_storage(), __rhs._M_storage());
              }
          }
        else
          {
-           _M_destructive_move(std::move(__rhs));
+           _Move_assign_base __tmp(__rhs);
+           this->~_Move_assign_base();
+           __try
+             {
+               ::new (this) _Move_assign_base(std::move(__tmp));
+             }
+           __catch (...)
+             {
+               this->_M_index = variant_npos;
+               __throw_exception_again;
+             }
          }
+       __glibcxx_assert(this->_M_index == __rhs._M_index);
        return *this;
       }
 
-      void*
-      _M_storage() const
-      {
-       return const_cast<void*>(static_cast<const void*>(
-           std::addressof(_Storage::_M_u)));
-      }
+      _Move_assign_base(const _Move_assign_base&) = default;
+      _Move_assign_base(_Move_assign_base&&) = default;
+      _Move_assign_base& operator=(const _Move_assign_base&) = default;
+    };
 
-      constexpr bool
-      _M_valid() const noexcept
-      {
-       return this->_M_index !=
-         typename _Storage::__index_type(variant_npos);
-      }
+  template<typename... _Types>
+    struct _Move_assign_base<true, _Types...> : _Copy_assign_alias<_Types...>
+    {
+      using _Base = _Copy_assign_alias<_Types...>;
+      using _Base::_Base;
+    };
+
+  template<typename... _Types>
+    using _Move_assign_alias =
+        _Move_assign_base<_Traits<_Types...>::_S_trivial_move_assign,
+                          _Types...>;
+
+  template<typename... _Types>
+    struct _Variant_base : _Move_assign_alias<_Types...>
+    {
+      using _Base = _Move_assign_alias<_Types...>;
+
+      constexpr
+      _Variant_base()
+          noexcept(_Traits<_Types...>::_S_nothrow_default_ctor)
+      : _Variant_base(in_place_index<0>) { }
+
+      template<size_t _Np, typename... _Args>
+       constexpr explicit
+       _Variant_base(in_place_index_t<_Np> __i, _Args&&... __args)
+       : _Base(__i, std::forward<_Args>(__args)...)
+       { }
+
+      _Variant_base(const _Variant_base&) = default;
+      _Variant_base(_Variant_base&&) = default;
+      _Variant_base& operator=(const _Variant_base&) = default;
+      _Variant_base& operator=(_Variant_base&&) = default;
     };
 
   // For how many times does _Tp appear in _Tuple?
@@ -882,16 +1041,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     class variant
     : private __detail::__variant::_Variant_base<_Types...>,
       private _Enable_default_constructor<
-       is_default_constructible_v<
-         variant_alternative_t<0, variant<_Types...>>>, variant<_Types...>>,
+        __detail::__variant::_Traits<_Types...>::_S_default_ctor,
+         variant<_Types...>>,
       private _Enable_copy_move<
-       (is_copy_constructible_v<_Types> && ...),
-       (is_copy_constructible_v<_Types> && ...)
-            && (is_move_constructible_v<_Types> && ...)
-            && (is_copy_assignable_v<_Types> && ...),
-       (is_move_constructible_v<_Types> && ...),
-       (is_move_constructible_v<_Types> && ...)
-            && (is_move_assignable_v<_Types> && ...),
+        __detail::__variant::_Traits<_Types...>::_S_copy_ctor,
+        __detail::__variant::_Traits<_Types...>::_S_copy_assign,
+        __detail::__variant::_Traits<_Types...>::_S_move_ctor,
+        __detail::__variant::_Traits<_Types...>::_S_move_assign,
        variant<_Types...>>
     {
     private:
@@ -904,9 +1060,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       using _Base = __detail::__variant::_Variant_base<_Types...>;
       using _Default_ctor_enabler =
-       _Enable_default_constructor<
-         is_default_constructible_v<
-           variant_alternative_t<0, variant<_Types...>>>, variant<_Types...>>;
+        _Enable_default_constructor<
+          __detail::__variant::_Traits<_Types...>::_S_default_ctor,
+            variant<_Types...>>;
 
       template<typename _Tp>
        static constexpr bool
@@ -933,12 +1089,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        static constexpr size_t __index_of =
          __detail::__variant::__index_of_v<_Tp, _Types...>;
 
+      using _Traits = __detail::__variant::_Traits<_Types...>;
+
     public:
-      constexpr variant()
-      noexcept(is_nothrow_default_constructible_v<__to_type<0>>) = default;
-      variant(const variant&) = default;
-      variant(variant&&)
-      noexcept((is_nothrow_move_constructible_v<_Types> && ...)) = default;
+      variant() = default;
+      variant(const variant& __rhs) = default;
+      variant(variant&&) = default;
+      variant& operator=(const variant&) = default;
+      variant& operator=(variant&&) = default;
+      ~variant() = default;
 
       template<typename _Tp,
               typename = enable_if_t<!is_same_v<decay_t<_Tp>, variant>>,
@@ -947,7 +1106,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        constexpr
        variant(_Tp&& __t)
        noexcept(is_nothrow_constructible_v<__accepted_type<_Tp&&>, _Tp&&>)
-       : variant(in_place_index<__accepted_index<_Tp&&>>, std::forward<_Tp>(__t))
+       : variant(in_place_index<__accepted_index<_Tp&&>>,
+                  std::forward<_Tp>(__t))
        { __glibcxx_assert(holds_alternative<__accepted_type<_Tp&&>>(*this)); }
 
       template<typename _Tp, typename... _Args,
@@ -955,7 +1115,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                          && is_constructible_v<_Tp, _Args&&...>>>
        constexpr explicit
        variant(in_place_type_t<_Tp>, _Args&&... __args)
-       : variant(in_place_index<__index_of<_Tp>>, std::forward<_Args>(__args)...)
+       : variant(in_place_index<__index_of<_Tp>>,
+                  std::forward<_Args>(__args)...)
        { __glibcxx_assert(holds_alternative<_Tp>(*this)); }
 
       template<typename _Tp, typename _Up, typename... _Args,
@@ -988,13 +1149,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        _Default_ctor_enabler(_Enable_default_constructor_tag{})
        { __glibcxx_assert(index() == _Np); }
 
-      ~variant() = default;
-
-      variant& operator=(const variant&) = default;
-      variant& operator=(variant&&)
-      noexcept((is_nothrow_move_constructible_v<_Types> && ...)
-         && (is_nothrow_move_assignable_v<_Types> && ...)) = default;
-
       template<typename _Tp>
        enable_if_t<__exactly_once<__accepted_type<_Tp&&>>
                    && is_constructible_v<__accepted_type<_Tp&&>, _Tp&&>
@@ -1089,7 +1243,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       constexpr size_t index() const noexcept
       {
        if (this->_M_index ==
-           typename _Base::_Storage::__index_type(variant_npos))
+           typename _Base::__index_type(variant_npos))
          return variant_npos;
        return this->_M_index;
       }
index 06e8eb31ee88dbf9b0305b0d16667422480e4170..e5f7538ba42c69da496d277c560e2ac56926829f 100644 (file)
@@ -88,10 +88,12 @@ void copy_ctor()
 {
   static_assert(is_copy_constructible_v<variant<int, string>>, "");
   static_assert(!is_copy_constructible_v<variant<AllDeleted, string>>, "");
+  static_assert(is_trivially_copy_constructible_v<variant<int>>, "");
+  static_assert(!is_trivially_copy_constructible_v<variant<std::string>>, "");
 
   {
     variant<int> a;
-    static_assert(!noexcept(variant<int>(a)), "");
+    static_assert(noexcept(variant<int>(a)), "");
   }
   {
     variant<string> a;
@@ -103,7 +105,7 @@ void copy_ctor()
   }
   {
     variant<int, char> a;
-    static_assert(!noexcept(variant<int, char>(a)), "");
+    static_assert(noexcept(variant<int, char>(a)), "");
   }
 }
 
@@ -111,6 +113,8 @@ void move_ctor()
 {
   static_assert(is_move_constructible_v<variant<int, string>>, "");
   static_assert(!is_move_constructible_v<variant<AllDeleted, string>>, "");
+  static_assert(is_trivially_move_constructible_v<variant<int>>, "");
+  static_assert(!is_trivially_move_constructible_v<variant<std::string>>, "");
   static_assert(!noexcept(variant<int, Empty>(declval<variant<int, Empty>>())), "");
   static_assert(noexcept(variant<int, DefaultNoexcept>(declval<variant<int, DefaultNoexcept>>())), "");
 }
@@ -148,13 +152,15 @@ void copy_assign()
 {
   static_assert(is_copy_assignable_v<variant<int, string>>, "");
   static_assert(!is_copy_assignable_v<variant<AllDeleted, string>>, "");
+  static_assert(is_trivially_copy_assignable_v<variant<int>>, "");
+  static_assert(!is_trivially_copy_assignable_v<variant<string>>, "");
   {
     variant<Empty> a;
     static_assert(!noexcept(a = a), "");
   }
   {
     variant<DefaultNoexcept> a;
-    static_assert(!noexcept(a = a), "");
+    static_assert(noexcept(a = a), "");
   }
 }
 
@@ -162,6 +168,8 @@ void move_assign()
 {
   static_assert(is_move_assignable_v<variant<int, string>>, "");
   static_assert(!is_move_assignable_v<variant<AllDeleted, string>>, "");
+  static_assert(is_trivially_move_assignable_v<variant<int>>, "");
+  static_assert(!is_trivially_move_assignable_v<variant<string>>, "");
   {
     variant<Empty> a;
     static_assert(!noexcept(a = std::move(a)), "");
@@ -454,3 +462,92 @@ void test_emplace()
   static_assert(!has_type_emplace<variant<AllDeleted>, AllDeleted>(0), "");
   static_assert(!has_index_emplace<variant<AllDeleted>, 0>(0), "");
 }
+
+void test_triviality()
+{
+#define TEST_TEMPLATE(DT, CC, MC, CA, MA, CC_VAL, MC_VAL, CA_VAL, MA_VAL) \
+  { \
+    struct A \
+    { \
+      ~A() DT; \
+      A(const A&) CC; \
+      A(A&&) MC; \
+      A& operator=(const A&) CA; \
+      A& operator=(A&&) MA; \
+    }; \
+    static_assert(CC_VAL == is_trivially_copy_constructible_v<variant<A>>, ""); \
+    static_assert(MC_VAL == is_trivially_move_constructible_v<variant<A>>, ""); \
+    static_assert(CA_VAL == is_trivially_copy_assignable_v<variant<A>>, ""); \
+    static_assert(MA_VAL == is_trivially_move_assignable_v<variant<A>>, ""); \
+  }
+  TEST_TEMPLATE(=default, =default, =default, =default, =default,  true,  true,  true,  true)
+  TEST_TEMPLATE(=default, =default, =default, =default,       {},  true,  true,  true, false)
+  TEST_TEMPLATE(=default, =default, =default,       {}, =default,  true,  true, false,  true)
+  TEST_TEMPLATE(=default, =default, =default,       {},       {},  true,  true, false, false)
+  TEST_TEMPLATE(=default, =default,       {}, =default, =default,  true, false,  true,  true)
+  TEST_TEMPLATE(=default, =default,       {}, =default,       {},  true, false,  true, false)
+  TEST_TEMPLATE(=default, =default,       {},       {}, =default,  true, false, false,  true)
+  TEST_TEMPLATE(=default, =default,       {},       {},       {},  true, false, false, false)
+  TEST_TEMPLATE(=default,       {}, =default, =default, =default, false,  true,  true,  true)
+  TEST_TEMPLATE(=default,       {}, =default, =default,       {}, false,  true,  true, false)
+  TEST_TEMPLATE(=default,       {}, =default,       {}, =default, false,  true, false,  true)
+  TEST_TEMPLATE(=default,       {}, =default,       {},       {}, false,  true, false, false)
+  TEST_TEMPLATE(=default,       {},       {}, =default, =default, false, false,  true,  true)
+  TEST_TEMPLATE(=default,       {},       {}, =default,       {}, false, false,  true, false)
+  TEST_TEMPLATE(=default,       {},       {},       {}, =default, false, false, false,  true)
+  TEST_TEMPLATE(=default,       {},       {},       {},       {}, false, false, false, false)
+  TEST_TEMPLATE(      {}, =default, =default, =default, =default, false, false, false, false)
+  TEST_TEMPLATE(      {}, =default, =default, =default,       {}, false, false, false, false)
+  TEST_TEMPLATE(      {}, =default, =default,       {}, =default, false, false, false, false)
+  TEST_TEMPLATE(      {}, =default, =default,       {},       {}, false, false, false, false)
+  TEST_TEMPLATE(      {}, =default,       {}, =default, =default, false, false, false, false)
+  TEST_TEMPLATE(      {}, =default,       {}, =default,       {}, false, false, false, false)
+  TEST_TEMPLATE(      {}, =default,       {},       {}, =default, false, false, false, false)
+  TEST_TEMPLATE(      {}, =default,       {},       {},       {}, false, false, false, false)
+  TEST_TEMPLATE(      {},       {}, =default, =default, =default, false, false, false, false)
+  TEST_TEMPLATE(      {},       {}, =default, =default,       {}, false, false, false, false)
+  TEST_TEMPLATE(      {},       {}, =default,       {}, =default, false, false, false, false)
+  TEST_TEMPLATE(      {},       {}, =default,       {},       {}, false, false, false, false)
+  TEST_TEMPLATE(      {},       {},       {}, =default, =default, false, false, false, false)
+  TEST_TEMPLATE(      {},       {},       {}, =default,       {}, false, false, false, false)
+  TEST_TEMPLATE(      {},       {},       {},       {}, =default, false, false, false, false)
+  TEST_TEMPLATE(      {},       {},       {},       {},       {}, false, false, false, false)
+#undef TEST_TEMPLATE
+
+#define TEST_TEMPLATE(CC, MC, CA, MA) \
+  { \
+    struct A \
+    { \
+      A(const A&) CC; \
+      A(A&&) MC; \
+      A& operator=(const A&) CA; \
+      A& operator=(A&&) MA; \
+    }; \
+    static_assert(!is_trivially_copy_constructible_v<variant<AllDeleted, A>>, ""); \
+    static_assert(!is_trivially_move_constructible_v<variant<AllDeleted, A>>, ""); \
+    static_assert(!is_trivially_copy_assignable_v<variant<AllDeleted, A>>, ""); \
+    static_assert(!is_trivially_move_assignable_v<variant<AllDeleted, A>>, ""); \
+  }
+  TEST_TEMPLATE(=default, =default, =default, =default)
+  TEST_TEMPLATE(=default, =default, =default,       {})
+  TEST_TEMPLATE(=default, =default,       {}, =default)
+  TEST_TEMPLATE(=default, =default,       {},       {})
+  TEST_TEMPLATE(=default,       {}, =default, =default)
+  TEST_TEMPLATE(=default,       {}, =default,       {})
+  TEST_TEMPLATE(=default,       {},       {}, =default)
+  TEST_TEMPLATE(=default,       {},       {},       {})
+  TEST_TEMPLATE(      {}, =default, =default, =default)
+  TEST_TEMPLATE(      {}, =default, =default,       {})
+  TEST_TEMPLATE(      {}, =default,       {}, =default)
+  TEST_TEMPLATE(      {}, =default,       {},       {})
+  TEST_TEMPLATE(      {},       {}, =default, =default)
+  TEST_TEMPLATE(      {},       {}, =default,       {})
+  TEST_TEMPLATE(      {},       {},       {}, =default)
+  TEST_TEMPLATE(      {},       {},       {},       {})
+#undef TEST_TEMPLATE
+
+  static_assert(is_trivially_copy_constructible_v<variant<DefaultNoexcept, int, char, float, double>>, "");
+  static_assert(is_trivially_move_constructible_v<variant<DefaultNoexcept, int, char, float, double>>, "");
+  static_assert(is_trivially_copy_assignable_v<variant<DefaultNoexcept, int, char, float, double>>, "");
+  static_assert(is_trivially_move_assignable_v<variant<DefaultNoexcept, int, char, float, double>>, "");
+}