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