On Wed, 11 Jun 2025 at 10:42, Tomasz Kaminski <[email protected]> wrote:
>
>
>
> On Tue, Jun 10, 2025 at 6:17 PM Jonathan Wakely <[email protected]> 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
>>