]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Use concepts to simplify std::optional base classes
authorJonathan Wakely <jwakely@redhat.com>
Mon, 22 Jul 2024 19:40:17 +0000 (20:40 +0100)
committerThomas Koenig <tkoenig@gcc.gnu.org>
Sun, 28 Jul 2024 17:05:56 +0000 (19:05 +0200)
In C++20 mode we can simplify some of the std::optional base class
hierarchy using concepts. We can overload the destructor and copy
constructor and move constructor with a trivial defaulted version and a
constrained non-trivial version. This allows us to remove some class
template partial specializations that were used to conditionally define
those special members as trivial or non-trivial. This should not change
any semantics, but should be less work for the compiler, due to not
needing to match partial specializations, and completely removing one
level of the inheritance hierarchy.

libstdc++-v3/ChangeLog:

* include/std/optional (_Optional_payload_base::_Storage)
[C++20]: Define constrained non-trivial destructor.
(_Optional_payload_base::_Storage<U, false>) [C++20]: Do not
define partial specialization when primary template has
constrained destructor.
(_Optional_base) [C++20]: Define constrained trivial copy and
move cons and move constructors. Define payload accessors here
instead of inheriting them from _Optional_base_impl.
(_Optional_base_impl, _Optional_base<T, false, true>)
(_Optional_base<T, true, false>, _Optional_base<T, true, true>)
[C++20]: Do not define.

libstdc++-v3/include/std/optional

index 9ed7ab501402e4ebdcfac04e0158cd382d37a26e..344be5e44d3ec54cfa812c3a637011366c5737bd 100644 (file)
@@ -226,10 +226,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
            { }
 #endif
 
+#if __cpp_concepts >= 202002L // Conditionally trivial special member functions
+         ~_Storage() = default;
+
+         // User-provided destructor is needed when _Up has non-trivial dtor.
+         _GLIBCXX20_CONSTEXPR
+         ~_Storage() requires (!is_trivially_destructible_v<_Up>)
+         { }
+
+         _Storage(const _Storage&) = default;
+         _Storage(_Storage&&) = default;
+         _Storage& operator=(const _Storage&) = default;
+         _Storage& operator=(_Storage&&) = default;
+#endif
+
          _Empty_byte _M_empty;
          _Up _M_value;
        };
 
+#if __cpp_concepts < 202002L
       template<typename _Up>
        union _Storage<_Up, false>
        {
@@ -259,9 +274,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          // User-provided destructor is needed when _Up has non-trivial dtor.
          _GLIBCXX20_CONSTEXPR ~_Storage() { }
 
+         _Storage(const _Storage&) = default;
+         _Storage(_Storage&&) = default;
+         _Storage& operator=(const _Storage&) = default;
+         _Storage& operator=(_Storage&&) = default;
+
          _Empty_byte _M_empty;
          _Up _M_value;
        };
+#endif
 
       _Storage<_Stored_type> _M_payload;
 
@@ -438,47 +459,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _GLIBCXX20_CONSTEXPR ~_Optional_payload() { this->_M_reset(); }
     };
 
-  // Common base class for _Optional_base<T> to avoid repeating these
-  // member functions in each specialization.
-  template<typename _Tp, typename _Dp>
-    class _Optional_base_impl
-    {
-    protected:
-      using _Stored_type = remove_const_t<_Tp>;
-
-      // The _M_construct operation has !_M_engaged as a precondition
-      // while _M_destruct has _M_engaged as a precondition.
-      template<typename... _Args>
-       constexpr void
-       _M_construct(_Args&&... __args)
-       noexcept(is_nothrow_constructible_v<_Stored_type, _Args...>)
-       {
-         static_cast<_Dp*>(this)->_M_payload._M_construct(
-           std::forward<_Args>(__args)...);
-       }
-
-      constexpr void
-      _M_destruct() noexcept
-      { static_cast<_Dp*>(this)->_M_payload._M_destroy(); }
-
-      // _M_reset is a 'safe' operation with no precondition.
-      constexpr void
-      _M_reset() noexcept
-      { static_cast<_Dp*>(this)->_M_payload._M_reset(); }
-
-      constexpr bool _M_is_engaged() const noexcept
-      { return static_cast<const _Dp*>(this)->_M_payload._M_engaged; }
-
-      // The _M_get operations have _M_engaged as a precondition.
-      constexpr _Tp&
-      _M_get() noexcept
-      { return static_cast<_Dp*>(this)->_M_payload._M_get(); }
-
-      constexpr const _Tp&
-      _M_get() const noexcept
-      { return static_cast<const _Dp*>(this)->_M_payload._M_get(); }
-    };
-
   /**
     * @brief Class template that provides copy/move constructors of optional.
     *
@@ -501,7 +481,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
           bool = is_trivially_copy_constructible_v<_Tp>,
           bool = is_trivially_move_constructible_v<_Tp>>
     struct _Optional_base
-    : _Optional_base_impl<_Tp, _Optional_base<_Tp>>
     {
       // Constructors for disengaged optionals.
       constexpr _Optional_base() = default;
@@ -528,6 +507,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // Copy and move constructors.
       constexpr
       _Optional_base(const _Optional_base& __other)
+      noexcept(is_nothrow_copy_constructible_v<_Tp>)
       : _M_payload(__other._M_payload._M_engaged, __other._M_payload)
       { }
 
@@ -538,15 +518,110 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                   std::move(__other._M_payload))
       { }
 
+#if __cpp_concepts >= 202002L // Conditionally trivial special member functions
+      // Define these in the primary template if possible, so that we don't
+      // need to use partial specializations of this class template.
+      constexpr _Optional_base(const _Optional_base&)
+       requires is_trivially_copy_constructible_v<_Tp> = default;
+
+      constexpr _Optional_base(_Optional_base&&)
+       requires is_trivially_move_constructible_v<_Tp> = default;
+#endif
+
       // Assignment operators.
       _Optional_base& operator=(const _Optional_base&) = default;
       _Optional_base& operator=(_Optional_base&&) = default;
 
       _Optional_payload<_Tp> _M_payload;
+
+    protected:
+      // For the primary template, we define these functions here.
+      using _Stored_type = remove_const_t<_Tp>;
+
+      // The _M_construct operation has !_M_engaged as a precondition
+      // while _M_destruct has _M_engaged as a precondition.
+      template<typename... _Args>
+       constexpr void
+       _M_construct(_Args&&... __args)
+       noexcept(is_nothrow_constructible_v<_Stored_type, _Args...>)
+       {
+         _M_payload._M_construct(std::forward<_Args>(__args)...);
+       }
+
+      constexpr void
+      _M_destruct() noexcept
+      { _M_payload._M_destroy(); }
+
+      // _M_reset is a 'safe' operation with no precondition.
+      constexpr void
+      _M_reset() noexcept
+      { _M_payload._M_reset(); }
+
+      constexpr bool _M_is_engaged() const noexcept
+      { return _M_payload._M_engaged; }
+
+      // The _M_get operations have _M_engaged as a precondition.
+      constexpr _Tp&
+      _M_get() noexcept
+      { return _M_payload._M_get(); }
+
+      constexpr const _Tp&
+      _M_get() const noexcept
+      { return _M_payload._M_get(); }
+    };
+
+#if __cpp_concepts < 202002L
+  // If P0848R3 "Conditionally Trivial Special Member Functions" is not
+  // supported (as determined from the __cpp_concepts macro value), the
+  // _Optional_base primary template only has non-trivial copy and move
+  // constructors. Use partial specializations of _Optional_base<T, C, M>
+  // that have a trivial copy and/or move constructor.
+
+  // Common base class for _Optional_base<T> to avoid repeating these
+  // member functions in each partial specialization.
+  // Only used if P0848R3 "Conditionally Trivial Special Member Functions"
+  // is not supported, as indicated by the __cpp_concepts value.
+  template<typename _Tp, typename _Dp>
+    class _Optional_base_impl
+    {
+    protected:
+      using _Stored_type = remove_const_t<_Tp>;
+
+      // The _M_construct operation has !_M_engaged as a precondition
+      // while _M_destruct has _M_engaged as a precondition.
+      template<typename... _Args>
+       constexpr void
+       _M_construct(_Args&&... __args)
+       noexcept(is_nothrow_constructible_v<_Stored_type, _Args...>)
+       {
+         static_cast<_Dp*>(this)->_M_payload._M_construct(
+           std::forward<_Args>(__args)...);
+       }
+
+      constexpr void
+      _M_destruct() noexcept
+      { static_cast<_Dp*>(this)->_M_payload._M_destroy(); }
+
+      // _M_reset is a 'safe' operation with no precondition.
+      constexpr void
+      _M_reset() noexcept
+      { static_cast<_Dp*>(this)->_M_payload._M_reset(); }
+
+      constexpr bool _M_is_engaged() const noexcept
+      { return static_cast<const _Dp*>(this)->_M_payload._M_engaged; }
+
+      // The _M_get operations have _M_engaged as a precondition.
+      constexpr _Tp&
+      _M_get() noexcept
+      { return static_cast<_Dp*>(this)->_M_payload._M_get(); }
+
+      constexpr const _Tp&
+      _M_get() const noexcept
+      { return static_cast<const _Dp*>(this)->_M_payload._M_get(); }
     };
 
   template<typename _Tp>
-    struct _Optional_base<_Tp, false, true>
+    struct _Optional_base<_Tp, false, true> // trivial move ctor
     : _Optional_base_impl<_Tp, _Optional_base<_Tp>>
     {
       // Constructors for disengaged optionals.
@@ -586,7 +661,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   template<typename _Tp>
-    struct _Optional_base<_Tp, true, false>
+    struct _Optional_base<_Tp, true, false> // trivial copy ctor
     : _Optional_base_impl<_Tp, _Optional_base<_Tp>>
     {
       // Constructors for disengaged optionals.
@@ -629,7 +704,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   template<typename _Tp>
-    struct _Optional_base<_Tp, true, true>
+    struct _Optional_base<_Tp, true, true> // trivial copy and move ctors
     : _Optional_base_impl<_Tp, _Optional_base<_Tp>>
     {
       // Constructors for disengaged optionals.
@@ -664,6 +739,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       _Optional_payload<_Tp> _M_payload;
     };
+#endif // __cpp_concepts
 
   template<typename _Tp>
   class optional;