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. Reviewed-by: Tomasz KamiĆski <tkami...@redhat.com> --- No changes since [PATCH v2], it's just been rebased because some of the removed code was changed slightly on trunk so the PATCH v2 no longer applies cleanly. This still causes: FAIL: g++.dg/pr104547.C -std=gnu++17 scan-tree-dump-not vrp2 "_M_default_append" and I don't know why the assembly contains _M_default_append even though it appears to be unused. This is beyond my ability to inspect optimized tree dumps so I need some help here. libstdc++-v3/include/bits/stl_uninitialized.h | 133 +++++------------- 1 file changed, 34 insertions(+), 99 deletions(-) diff --git a/libstdc++-v3/include/bits/stl_uninitialized.h b/libstdc++-v3/include/bits/stl_uninitialized.h index 351c3a17457f..51974ad2b2a6 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 @@ -849,98 +848,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> _GLIBCXX20_CONSTEXPR inline void - __uninitialized_default(_ForwardIterator __first, - _ForwardIterator __last) + __uninitialized_default(_ForwardIterator __first, _ForwardIterator __last) { -#ifdef __cpp_lib_is_constant_evaluated - if (std::is_constant_evaluated()) - return __uninitialized_default_1<false>:: - __uninit_default(__first, __last); -#endif + using _ValueType = typename iterator_traits<_ForwardIterator>::value_type; - typedef typename iterator_traits<_ForwardIterator>::value_type - _ValueType; - // trivial types can have deleted assignment - const bool __assignable = is_copy_assignable<_ValueType>::value; + 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); + } - std::__uninitialized_default_1<__is_trivial(_ValueType) - && __assignable>:: - __uninit_default(__first, __last); + _UninitDestroyGuard<_ForwardIterator> __guard(__first); + for (; __first != __last; ++__first) + std::_Construct(std::addressof(*__first)); + __guard.release(); } // __uninitialized_default_n @@ -950,23 +882,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.50.0