Tested x86_64-linux.

This should improve compile times for C++20 and up.

I need to test this with Clang, but then I plan to push it if all goes
well.

-- >8 --

For C++20 the __detail::__variant::_Uninitialized primary template can
be used for all types, because _Variant_union can have a non-trivially
destructible union member in C++20, and the constrained user-provided
destructor will ensure we don't destroy inactive objects.

Since we always use the primary template for C++20, we don't need the
_Uninitialized::_M_get accessors to abstract the difference between the
primary template and the partial specialization. That allows us to
simplify __get_n for C++20 too.

Also improve the comments that explain the uses of _Uninitialized and
when/why _Variant_union needs a user-provided destructor.

libstdc++-v3/ChangeLog:

        * include/std/variant [C++20] (_Uninitialized): Always use the
        primary template.
        [C++20] (__get_n): Access the _M_storage member directly.
---
 libstdc++-v3/include/std/variant | 83 ++++++++++++++------------------
 1 file changed, 37 insertions(+), 46 deletions(-)

diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant
index 5fb7770d889..08c5395b54d 100644
--- a/libstdc++-v3/include/std/variant
+++ b/libstdc++-v3/include/std/variant
@@ -54,10 +54,9 @@
 
 // C++ < 20 || __cpp_concepts < 202002L || __cpp_constexpr < 201811L
 #if __cpp_lib_variant < 202106L
-# include <ext/aligned_buffer.h> // Use __aligned_membuf instead of union.
+# include <ext/aligned_buffer.h> // Use __aligned_membuf for storage.
 #endif
 
-
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -209,17 +208,18 @@ namespace __variant
     __as(const std::variant<_Types...>&& __v) noexcept
     { return std::move(__v); }
 
-  // For C++17:
-  // _Uninitialized<T> is guaranteed to be a trivially destructible type,
-  // even if T is not.
-  // For C++20:
-  // _Uninitialized<T> is trivially destructible iff T is, so _Variant_union
-  // needs a constrained non-trivial destructor.
+#if __cpp_lib_variant < 202106L
   template<typename _Type, bool = std::is_trivially_destructible_v<_Type>>
     struct _Uninitialized;
+#else
+  template<typename _Type, bool /* unused */ = true>
+    struct _Uninitialized;
+#endif
 
-  template<typename _Type>
-    struct _Uninitialized<_Type, true>
+  // The primary template is used for trivially destructible types in C++17,
+  // and for all types in C++20.
+  template<typename _Type, bool>
+    struct _Uninitialized
     {
       template<typename... _Args>
        constexpr
@@ -227,6 +227,7 @@ namespace __variant
        : _M_storage(std::forward<_Args>(__args)...)
        { }
 
+#if __cpp_lib_variant < 202106L
       constexpr const _Type& _M_get() const & noexcept
       { return _M_storage; }
 
@@ -238,46 +239,18 @@ namespace __variant
 
       constexpr _Type&& _M_get() && noexcept
       { return std::move(_M_storage); }
+#endif
 
       _Type _M_storage;
     };
 
+#if __cpp_lib_variant < 202106L
+  // This partial specialization is used for non-trivially destructible types
+  // in C++17, so that _Uninitialized<T> is trivially destructible and can be
+  // used as a union member in _Variadic_union.
   template<typename _Type>
     struct _Uninitialized<_Type, false>
     {
-#if __cpp_lib_variant >= 202106L
-      template<typename... _Args>
-       constexpr
-       _Uninitialized(in_place_index_t<0>, _Args&&... __args)
-       : _M_storage(std::forward<_Args>(__args)...)
-       { }
-
-      constexpr ~_Uninitialized() { }
-
-      _Uninitialized(const _Uninitialized&) = default;
-      _Uninitialized(_Uninitialized&&) = default;
-      _Uninitialized& operator=(const _Uninitialized&) = default;
-      _Uninitialized& operator=(_Uninitialized&&) = default;
-
-      constexpr const _Type& _M_get() const & noexcept
-      { return _M_storage; }
-
-      constexpr _Type& _M_get() & noexcept
-      { return _M_storage; }
-
-      constexpr const _Type&& _M_get() const && noexcept
-      { return std::move(_M_storage); }
-
-      constexpr _Type&& _M_get() && noexcept
-      { return std::move(_M_storage); }
-
-      struct _Empty_byte { };
-
-      union {
-       _Empty_byte _M_empty;
-       _Type _M_storage;
-      };
-#else
       template<typename... _Args>
        constexpr
        _Uninitialized(in_place_index_t<0>, _Args&&... __args)
@@ -299,7 +272,6 @@ namespace __variant
       { return std::move(*_M_storage._M_ptr()); }
 
       __gnu_cxx::__aligned_membuf<_Type> _M_storage;
-#endif
     };
 
   template<size_t _Np, typename _Union>
@@ -316,6 +288,22 @@ namespace __variant
        return __variant::__get_n<_Np - 3>(
                 std::forward<_Union>(__u)._M_rest._M_rest._M_rest);
     }
+#else
+  template<size_t _Np, typename _Union>
+    constexpr auto&&
+    __get_n(_Union&& __u) noexcept
+    {
+      if constexpr (_Np == 0)
+       return std::forward<_Union>(__u)._M_first._M_storage;
+      else if constexpr (_Np == 1)
+       return std::forward<_Union>(__u)._M_rest._M_first._M_storage;
+      else if constexpr (_Np == 2)
+       return std::forward<_Union>(__u)._M_rest._M_rest._M_first._M_storage;
+      else
+       return __variant::__get_n<_Np - 3>(
+                std::forward<_Union>(__u)._M_rest._M_rest._M_rest);
+    }
+#endif
 
   // Returns the typed storage for __v.
   template<size_t _Np, typename _Variant>
@@ -428,6 +416,9 @@ namespace __variant
 
       ~_Variadic_union() = default;
 
+      // If any alternative type is not trivially destructible then we need a
+      // user-provided destructor that does nothing. The active alternative
+      // will be destroyed by _Variant_storage::_M_reset() instead of here.
       constexpr ~_Variadic_union()
        requires (!__trivially_destructible)
       { }
@@ -486,7 +477,7 @@ namespace __variant
        constexpr
        _Variant_storage(in_place_index_t<_Np>, _Args&&... __args)
        : _M_u(in_place_index<_Np>, std::forward<_Args>(__args)...),
-       _M_index{_Np}
+         _M_index{_Np}
        { }
 
       constexpr void
@@ -532,7 +523,7 @@ namespace __variant
        constexpr
        _Variant_storage(in_place_index_t<_Np>, _Args&&... __args)
        : _M_u(in_place_index<_Np>, std::forward<_Args>(__args)...),
-       _M_index{_Np}
+         _M_index{_Np}
        { }
 
       constexpr void
-- 
2.46.0

Reply via email to