On Wed, Jun 11, 2025 at 1:14 PM Jonathan Wakely <jwak...@redhat.com> wrote:

> 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.
>
Looks good. Thanks.

>
>  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
>
>

Reply via email to