Use diagnostic pragmas to allow using `if constexpr` in C++11 mode, so
that we don't need to use tag dispatching.

The unused member functions are preserved for the purposes of explicit
instantiations. The _M_assign function template can be removed, because
member function templates aren't instantiated by explicit instantiations
anyway.

libstdc++-v3/ChangeLog:

        * include/bits/forward_list.h (operator=(forward_list&&)): Use
        if constexpr instead of dispatching to _M_move_assign.
        (assign(InputIterator, InputIterator)): Use if constexpr instead
        of dispatching to _M_assign.
        (assign(size_type, const T&)): Use if constexpr instead of
        dispatching to _M_assign_n.
        (_M_move_assign, _M_assign_n): Do not define for versioned
        namespace.
        (_M_assign): Remove.
---

Tested x86_64-linux. Pushed to trunk.

 libstdc++-v3/include/bits/forward_list.h | 119 +++++++++++++++--------
 1 file changed, 79 insertions(+), 40 deletions(-)

diff --git a/libstdc++-v3/include/bits/forward_list.h 
b/libstdc++-v3/include/bits/forward_list.h
index 663ed0c46af..ac1b3593c79 100644
--- a/libstdc++-v3/include/bits/forward_list.h
+++ b/libstdc++-v3/include/bits/forward_list.h
@@ -644,6 +644,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       forward_list&
       operator=(const forward_list& __list);
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
       /**
        *  @brief  The %forward_list move assignment operator.
        *  @param  __list  A %forward_list of identical element and allocator
@@ -663,7 +665,24 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
        constexpr bool __move_storage =
          _Node_alloc_traits::_S_propagate_on_move_assign()
          || _Node_alloc_traits::_S_always_equal();
-       _M_move_assign(std::move(__list), __bool_constant<__move_storage>());
+       if constexpr (!__move_storage)
+         {
+           if (__list._M_get_Node_allocator() != this->_M_get_Node_allocator())
+             {
+               // The rvalue's allocator cannot be moved, or is not equal,
+               // so we need to individually move each element.
+               this->assign(std::make_move_iterator(__list.begin()),
+                            std::make_move_iterator(__list.end()));
+               return *this;
+             }
+         }
+
+       clear();
+       this->_M_impl._M_head._M_next = __list._M_impl._M_head._M_next;
+       __list._M_impl._M_head._M_next = nullptr;
+       if constexpr (_Node_alloc_traits::_S_propagate_on_move_assign())
+         this->_M_get_Node_allocator()
+             = std::move(__list._M_get_Node_allocator());
        return *this;
       }
 
@@ -699,9 +718,30 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
        void
        assign(_InputIterator __first, _InputIterator __last)
        {
-         typedef is_assignable<_Tp, decltype(*__first)> __assignable;
-         _M_assign(__first, __last, __assignable());
+         if constexpr (is_assignable<_Tp, decltype(*__first)>::value)
+           {
+             auto __prev = before_begin();
+             auto __curr = begin();
+             auto __end = end();
+             while (__curr != __end && __first != __last)
+               {
+                 *__curr = *__first;
+                 ++__prev;
+                 ++__curr;
+                 ++__first;
+               }
+             if (__first != __last)
+               insert_after(__prev, __first, __last);
+             else if (__curr != __end)
+               erase_after(__prev, __end);
+           }
+         else
+           {
+             clear();
+             insert_after(cbefore_begin(), __first, __last);
+           }
        }
+#pragma GCC diagnostic pop
 
 #if __glibcxx_ranges_to_container // C++ >= 23
       /**
@@ -736,6 +776,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
        }
 #endif // ranges_to_container
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
       /**
        *  @brief  Assigns a given value to a %forward_list.
        *  @param  __n  Number of elements to be assigned.
@@ -748,7 +790,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
        */
       void
       assign(size_type __n, const _Tp& __val)
-      { _M_assign_n(__n, __val, is_copy_assignable<_Tp>()); }
+      {
+       if constexpr (is_copy_assignable<_Tp>::value)
+         {
+           auto __prev = before_begin();
+           auto __curr = begin();
+           auto __end = end();
+           while (__curr != __end && __n > 0)
+             {
+               *__curr = __val;
+               ++__prev;
+               ++__curr;
+               --__n;
+             }
+           if (__n > 0)
+             insert_after(__prev, __n, __val);
+           else if (__curr != __end)
+             erase_after(__prev, __end);
+         }
+       else
+         {
+           clear();
+           insert_after(cbefore_begin(), __n, __val);
+         }
+      }
+#pragma GCC diagnostic pop
 
       /**
        *  @brief  Assigns an initializer_list to a %forward_list.
@@ -1460,7 +1526,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       void
       _M_default_insert_after(const_iterator __pos, size_type __n);
 
-      // Called by operator=(forward_list&&)
+#if ! _GLIBCXX_INLINE_VERSION
+      // XXX GLIBCXX_ABI Deprecated
+      // These members are unused by std::forward_list now, but we keep them
+      // here so that an explicit instantiation will define them.
+      // This ensures that explicit instantiations still define these symbols,
+      // so that explicit instantiation declarations of std::forward_list that
+      // were compiled with old versions of GCC can still find these symbols.
+
       void
       _M_move_assign(forward_list&& __list, true_type) noexcept
       {
@@ -1471,7 +1544,6 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
                             __list._M_get_Node_allocator());
       }
 
-      // Called by operator=(forward_list&&)
       void
       _M_move_assign(forward_list&& __list, false_type)
       {
@@ -1484,39 +1556,6 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
                       std::make_move_iterator(__list.end()));
       }
 
-      // Called by assign(_InputIterator, _InputIterator) if _Tp is
-      // CopyAssignable.
-      template<typename _InputIterator>
-       void
-       _M_assign(_InputIterator __first, _InputIterator __last, true_type)
-       {
-         auto __prev = before_begin();
-         auto __curr = begin();
-         auto __end = end();
-         while (__curr != __end && __first != __last)
-           {
-             *__curr = *__first;
-             ++__prev;
-             ++__curr;
-             ++__first;
-           }
-         if (__first != __last)
-           insert_after(__prev, __first, __last);
-         else if (__curr != __end)
-           erase_after(__prev, __end);
-       }
-
-      // Called by assign(_InputIterator, _InputIterator) if _Tp is not
-      // CopyAssignable.
-      template<typename _InputIterator>
-       void
-       _M_assign(_InputIterator __first, _InputIterator __last, false_type)
-       {
-         clear();
-         insert_after(cbefore_begin(), __first, __last);
-       }
-
-      // Called by assign(size_type, const _Tp&) if Tp is CopyAssignable
       void
       _M_assign_n(size_type __n, const _Tp& __val, true_type)
       {
@@ -1536,13 +1575,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
          erase_after(__prev, __end);
       }
 
-      // Called by assign(size_type, const _Tp&) if Tp is non-CopyAssignable
       void
       _M_assign_n(size_type __n, const _Tp& __val, false_type)
       {
        clear();
        insert_after(cbefore_begin(), __n, __val);
       }
+#endif // ! _GLIBCXX_INLINE_VERSION
     };
 
 #if __cpp_deduction_guides >= 201606
-- 
2.47.0

Reply via email to