Index: libcxx/include/__memory/shared_ptr.h =================================================================== --- libcxx/include/__memory/shared_ptr.h +++ libcxx/include/__memory/shared_ptr.h @@ -1059,7 +1059,7 @@ explicit __unbounded_array_control_block(_Alloc const& __alloc, size_t __count, _Tp const& __arg) : __alloc_(__alloc), __count_(__count) { - std::__uninitialized_allocator_fill_n_multidimensional(__alloc_, std::begin(__data_), __count_, __arg); + std::__uninitialized_allocator_construct_n_multidimensional(__alloc_, std::begin(__data_), __count_, __arg); } _LIBCPP_HIDE_FROM_ABI @@ -1072,10 +1072,10 @@ // There's currently no way of expressing default initialization in an allocator-aware manner anyway. std::uninitialized_default_construct_n(std::begin(__data_), __count_); } else { - std::__uninitialized_allocator_value_construct_n_multidimensional(__alloc_, std::begin(__data_), __count_); + std::__uninitialized_allocator_construct_n_multidimensional(__alloc_, std::begin(__data_), __count_); } #else - std::__uninitialized_allocator_value_construct_n_multidimensional(__alloc_, std::begin(__data_), __count_); + std::__uninitialized_allocator_construct_n_multidimensional(__alloc_, std::begin(__data_), __count_); #endif } @@ -1163,7 +1163,7 @@ _LIBCPP_HIDE_FROM_ABI explicit __bounded_array_control_block(_Alloc const& __alloc, _Tp const& __arg) : __alloc_(__alloc) { - std::__uninitialized_allocator_fill_n_multidimensional(__alloc_, std::addressof(__data_[0]), _Count, __arg); + std::__uninitialized_allocator_construct_n_multidimensional(__alloc_, std::addressof(__data_[0]), _Count, __arg); } _LIBCPP_HIDE_FROM_ABI @@ -1174,10 +1174,10 @@ // There's currently no way of expressing default initialization in an allocator-aware manner anyway. std::uninitialized_default_construct_n(std::addressof(__data_[0]), _Count); } else { - std::__uninitialized_allocator_value_construct_n_multidimensional(__alloc_, std::addressof(__data_[0]), _Count); + std::__uninitialized_allocator_construct_n_multidimensional(__alloc_, std::addressof(__data_[0]), _Count); } #else - std::__uninitialized_allocator_value_construct_n_multidimensional(__alloc_, std::addressof(__data_[0]), _Count); + std::__uninitialized_allocator_construct_n_multidimensional(__alloc_, std::addressof(__data_[0]), _Count); #endif } Index: libcxx/include/__memory/uninitialized_algorithms.h =================================================================== --- libcxx/include/__memory/uninitialized_algorithms.h +++ libcxx/include/__memory/uninitialized_algorithms.h @@ -401,76 +401,35 @@ } } -// Constructs the object at the given location using the allocator's construct method. -// -// If the object being constructed is an array, each element of the array is allocator-constructed, -// recursively. If an exception is thrown during the construction of an array, the initialized -// elements are destroyed in reverse order of initialization using allocator destruction. -// -// This function assumes that the allocator is bound to the correct type. -template -_LIBCPP_HIDE_FROM_ABI -constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc) { - static_assert(is_same_v::value_type, _Tp>, - "The allocator should already be rebound to the correct type"); - - if constexpr (is_array_v<_Tp>) { - using _Element = remove_extent_t<_Tp>; - __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc); - size_t __i = 0; - _Tp& __array = *__loc; - - // If an exception is thrown, destroy what we have constructed so far in reverse order. - auto __guard = std::__make_exception_guard([&]() { - std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i); - }); - - for (; __i != extent_v<_Tp>; ++__i) { - std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i])); - } - __guard.__complete(); - } else { - allocator_traits<_Alloc>::construct(__alloc, __loc); - } -} - -// Constructs the object at the given location using the allocator's construct method, passing along -// the provided argument. -// -// If the object being constructed is an array, the argument is also assumed to be an array. Each -// each element of the array being constructed is allocator-constructed from the corresponding -// element of the argument array. If an exception is thrown during the construction of an array, -// the initialized elements are destroyed in reverse order of initialization using allocator -// destruction. -// -// This function assumes that the allocator is bound to the correct type. -template -_LIBCPP_HIDE_FROM_ABI -constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc, _Arg const& __arg) { - static_assert(is_same_v::value_type, _Tp>, - "The allocator should already be rebound to the correct type"); +template +struct _IsBoundedArray { static constexpr bool value = false; }; +template +struct _IsBoundedArray { static constexpr bool value = true; }; + +// For a type T[N][M] calculate T and N*M +template +struct __flatten +{ + using type = _Tp; + static constexpr int value = 1; +}; - if constexpr (is_array_v<_Tp>) { - static_assert(is_array_v<_Arg>, - "Provided non-array initialization argument to __allocator_construct_at_multidimensional when " - "trying to construct an array."); +template +struct __flatten<_Tp[N]> +{ + using type = typename __flatten<_Tp>::type; + static constexpr int value = __flatten<_Tp>::value * N; +}; - using _Element = remove_extent_t<_Tp>; - __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc); - size_t __i = 0; - _Tp& __array = *__loc; - - // If an exception is thrown, destroy what we have constructed so far in reverse order. - auto __guard = std::__make_exception_guard([&]() { - std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i); - }); - for (; __i != extent_v<_Tp>; ++__i) { - std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]), __arg[__i]); - } - __guard.__complete(); - } else { - allocator_traits<_Alloc>::construct(__alloc, __loc, __arg); - } +// Get the address of the first element of a possibly nested c-style array +template +_LIBCPP_HIDE_FROM_ABI constexpr +auto* __flat_addressof(T& t) +{ + if constexpr (_IsBoundedArray::value) + return __flat_addressof(t[0]); + else + return std::addressof(t); } // Given a range starting at it and containing n elements, initializes each element in the @@ -479,36 +438,42 @@ // // If an exception is thrown, the initialized elements are destroyed in reverse order of // initialization using allocator_traits destruction. If the elements in the range are C-style -// arrays, they are initialized element-wise using allocator construction, and recursively so. -template::difference_type> +// arrays, they are initialized element-wise using allocator construction by treating them as a +// single larger flat array +template::difference_type> _LIBCPP_HIDE_FROM_ABI constexpr void -__uninitialized_allocator_fill_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n, _Tp const& __value) { - using _ValueType = typename iterator_traits<_BidirIter>::value_type; - __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc); - _BidirIter __begin = __it; +__uninitialized_allocator_construct_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n, _Tp const&... __value) { - // If an exception is thrown, destroy what we have constructed so far in reverse order. - auto __guard = std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); }); - for (; __n != 0; --__n, ++__it) { - std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it), __value); - } - __guard.__complete(); -} + // Either default initialization or value initialization + static_assert(sizeof...(_Tp) <= 1); -// Same as __uninitialized_allocator_fill_n_multidimensional, but doesn't pass any initialization argument -// to the allocator's construct method, which results in value initialization. -template ::difference_type> -_LIBCPP_HIDE_FROM_ABI constexpr void -__uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n) { using _ValueType = typename iterator_traits<_BidirIter>::value_type; - __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc); - _BidirIter __begin = __it; + using _FlatValueType = typename __flatten<_ValueType>::type; + __n *= __flatten<_ValueType>::value; + + using _FValueAlloc = __allocator_traits_rebind_t<_Alloc, _FlatValueType>; + _FValueAlloc __fvalue_alloc(__alloc); + + auto* __fit = __flat_addressof(*__it); + _Size __idx = 0; + + auto __guard = std::__make_exception_guard([&]() { + for (; __idx != 0; --__idx) + allocator_traits<_FValueAlloc>::destroy(__fvalue_alloc, --__fit); + }); - // If an exception is thrown, destroy what we have constructed so far in reverse order. - auto __guard = std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); }); - for (; __n != 0; --__n, ++__it) { - std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it)); + if constexpr (sizeof...(_Tp) == 1) { + auto* __fill_ptr = __flat_addressof(__value...); + constexpr int __fill_elements = __flatten<_Tp...>::value; + + for (; __idx < __n; ++__fit, ++__idx) + allocator_traits<_FValueAlloc>::construct(__fvalue_alloc, __fit, __fill_ptr[__idx % __fill_elements]); + + } else { + for (; __idx < __n; ++__fit, ++__idx) + allocator_traits<_FValueAlloc>::construct(__fvalue_alloc, __fit); } + __guard.__complete(); }