On Wed, 11 Jun 2025 at 10:42, Tomasz Kaminski <tkami...@redhat.com> wrote: > > > > On Tue, Jun 10, 2025 at 6:17 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. >> --- >> >> Tested x86_64-linux. > > Only one stylitic comment. >> >> >> libstdc++-v3/include/bits/stl_uninitialized.h | 136 ++++++------------ >> 1 file changed, 42 insertions(+), 94 deletions(-) >> >> diff --git a/libstdc++-v3/include/bits/stl_uninitialized.h >> b/libstdc++-v3/include/bits/stl_uninitialized.h >> index bde787c2beaa..d5a399e16c90 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,36 @@ _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 (__first == __last) >> + return; > > This is different from _n, I would do them consistently and have: > if (!std::__is_constant_evaluated() && __first != __last)
That would mean repeating the __first != __last check below when we get to the loop and my intention was that once we enter the `if constexpr (trivial)` block we always return early ... but I just noticed that I failed to add a return after calling std::uninitialized_fill - oops! So it always falls through to the loop and checks __first != __last again anyway. I'll send a new patch ... >> >> + if (!std::__is_constant_evaluated()) >> + { >> + auto* __addr = std::addressof(*__first); >> + std::_Construct(__addr); >> + const auto& __val = *__addr; >> + if (++__first != __last) >> + 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 +867,28 @@ _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) >> + { > > We do not need nested braces hre. True. >> >> + 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 >>