[PATCH v2] libstdc++: Simplify __uninitialized_default and __uninitialized_default_n

Jonathan Wakely jwakely@redhat.com
Wed Jun 11 11:09:12 GMT 2025


With improved memset optimizations in std::uninitialized_fill and
std::uninitialized_fill_n (see r15-4473-g3abe751ea86e34), we can make
the non-standard internal helpers __uninitialized_default and
__uninitialized_default_n use those directly instead of using std::fill
and std::fill_n respectively. And if we do that, we no longer need to
check whether the type is assignable, because avoiding std::fill means
no assignment happens.

If the type being constructed is trivially default constructible and
trivially copy constructible, then it's unobservable if we construct one
object and copy it N-1 times, rather than constructing N objects. For
byte-sized integer types this allows the loop to be replaced with
memset.

Because these functions are not defined for C++98 at all, we can use
if-constexpr to simplify them and remove the dispatching to members of
class template specializations.

By removing the uses of std::fill and std::fill_n we no longer need to
include stl_algobase.h in stl_uninitialized.h which might improve
compilation time for some other parts of the library.

libstdc++-v3/ChangeLog:

	* include/bits/stl_uninitialized.h: Do not include
	bits/stl_algobase.h.
	(__uninitialized_default_1, __uninitialized_default_n_1):
	Remove.
	(__uninitialized_default, __uninitialized_default_n): Use
	'if constexpr' and only consider triviality constructibility
	not assignability when deciding on the algorithm to use.
---

v2: Make __uninitialized_default and __uninitialized_default_n use more
similar structure, returning early after using uninitialized_fill, and
without bothering to optimize the unlikely case where we're constructing
zero objects.

Tested x86_64-linux.

 libstdc++-v3/include/bits/stl_uninitialized.h | 129 +++++-------------
 1 file changed, 35 insertions(+), 94 deletions(-)

diff --git a/libstdc++-v3/include/bits/stl_uninitialized.h b/libstdc++-v3/include/bits/stl_uninitialized.h
index bde787c2beaa..9372e5a01847 100644
--- a/libstdc++-v3/include/bits/stl_uninitialized.h
+++ b/libstdc++-v3/include/bits/stl_uninitialized.h
@@ -60,7 +60,6 @@
 # include <type_traits>
 # include <bits/ptr_traits.h>      // to_address
 # include <bits/stl_pair.h>        // pair
-# include <bits/stl_algobase.h>    // fill, fill_n
 #endif
 
 #include <bits/cpp_type_traits.h> // __is_pointer
@@ -829,92 +828,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Extensions: __uninitialized_default, __uninitialized_default_n,
   // __uninitialized_default_a, __uninitialized_default_n_a.
 
-  template<bool _TrivialValueType>
-    struct __uninitialized_default_1
-    {
-      template<typename _ForwardIterator>
-        _GLIBCXX26_CONSTEXPR
-        static void
-        __uninit_default(_ForwardIterator __first, _ForwardIterator __last)
-        {
-	  _UninitDestroyGuard<_ForwardIterator> __guard(__first);
-	  for (; __first != __last; ++__first)
-	    std::_Construct(std::addressof(*__first));
-	  __guard.release();
-	}
-    };
-
-  template<>
-    struct __uninitialized_default_1<true>
-    {
-      template<typename _ForwardIterator>
-        _GLIBCXX26_CONSTEXPR
-        static void
-        __uninit_default(_ForwardIterator __first, _ForwardIterator __last)
-        {
-	  if (__first == __last)
-	    return;
-
-	  typename iterator_traits<_ForwardIterator>::value_type* __val
-	    = std::addressof(*__first);
-	  std::_Construct(__val);
-	  if (++__first != __last)
-	    std::fill(__first, __last, *__val);
-	}
-    };
-
-  template<bool _TrivialValueType>
-    struct __uninitialized_default_n_1
-    {
-      template<typename _ForwardIterator, typename _Size>
-	_GLIBCXX20_CONSTEXPR
-        static _ForwardIterator
-        __uninit_default_n(_ForwardIterator __first, _Size __n)
-        {
-	  _UninitDestroyGuard<_ForwardIterator> __guard(__first);
-	  for (; __n > 0; --__n, (void) ++__first)
-	    std::_Construct(std::addressof(*__first));
-	  __guard.release();
-	  return __first;
-	}
-    };
-
-  template<>
-    struct __uninitialized_default_n_1<true>
-    {
-      template<typename _ForwardIterator, typename _Size>
-	_GLIBCXX20_CONSTEXPR
-        static _ForwardIterator
-        __uninit_default_n(_ForwardIterator __first, _Size __n)
-        {
-	  if (__n > 0)
-	    {
-	      typename iterator_traits<_ForwardIterator>::value_type* __val
-		= std::addressof(*__first);
-	      std::_Construct(__val);
-	      ++__first;
-	      __first = std::fill_n(__first, __n - 1, *__val);
-	    }
-	  return __first;
-	}
-    };
-
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wc++17-extensions"
   // __uninitialized_default
   // Fills [first, last) with value-initialized value_types.
   template<typename _ForwardIterator>
     _GLIBCXX26_CONSTEXPR
     inline void
-    __uninitialized_default(_ForwardIterator __first,
-			    _ForwardIterator __last)
+    __uninitialized_default(_ForwardIterator __first, _ForwardIterator __last)
     {
-      typedef typename iterator_traits<_ForwardIterator>::value_type
-	_ValueType;
-      // trivial types can have deleted assignment
-      const bool __assignable = is_copy_assignable<_ValueType>::value;
+      using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
 
-      std::__uninitialized_default_1<__is_trivial(_ValueType)
-				     && __assignable>::
-	__uninit_default(__first, __last);
+      if constexpr (__and_<is_trivially_default_constructible<_ValueType>,
+			   is_trivially_copy_constructible<_ValueType>>::value)
+	if (!std::__is_constant_evaluated() && __first != __last)
+	  {
+	    auto* __addr = std::addressof(*__first);
+	    std::_Construct(__addr);
+	    const auto& __val = *__addr;
+	    return std::uninitialized_fill(++__first, __last, __val);
+	  }
+
+      _UninitDestroyGuard<_ForwardIterator> __guard(__first);
+      for (; __first != __last; ++__first)
+	std::_Construct(std::addressof(*__first));
+      __guard.release();
     }
 
   // __uninitialized_default_n
@@ -924,23 +862,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     inline _ForwardIterator
     __uninitialized_default_n(_ForwardIterator __first, _Size __n)
     {
-#ifdef __cpp_lib_is_constant_evaluated
-      if (std::is_constant_evaluated())
-	return __uninitialized_default_n_1<false>::
-		 __uninit_default_n(__first, __n);
-#endif
+      using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
 
-      typedef typename iterator_traits<_ForwardIterator>::value_type
-	_ValueType;
-      // See uninitialized_fill_n for the conditions for using std::fill_n.
-      constexpr bool __can_fill
-	= __and_<is_integral<_Size>, is_copy_assignable<_ValueType>>::value;
+      if constexpr (__and_<is_trivially_default_constructible<_ValueType>,
+			   is_trivially_copy_constructible<_ValueType>,
+			   is_integral<_Size>>::value)
+	if (!std::__is_constant_evaluated() && __n > 0)
+	  {
+	    auto* __addr = std::addressof(*__first);
+	    std::_Construct(__addr);
+	    const auto& __val = *__addr;
+	    return std::uninitialized_fill_n(++__first, --__n, __val);
+	  }
 
-      return __uninitialized_default_n_1<__is_trivial(_ValueType)
-					 && __can_fill>::
-	__uninit_default_n(__first, __n);
+      _UninitDestroyGuard<_ForwardIterator> __guard(__first);
+      for (; __n > 0; --__n, (void) ++__first)
+	std::_Construct(std::addressof(*__first));
+      __guard.release();
+      return __first;
     }
-
+#pragma GCC diagnostic pop
 
   // __uninitialized_default_a
   // Fills [first, last) with value_types constructed by the allocator
-- 
2.49.0



More information about the Libstdc++ mailing list