diff --git a/libcxx/docs/Status/RangesPaper.csv b/libcxx/docs/Status/RangesPaper.csv --- a/libcxx/docs/Status/RangesPaper.csv +++ b/libcxx/docs/Status/RangesPaper.csv @@ -12,14 +12,14 @@ | `iter_difference_t `_",,Christopher Di Bella,✅ `[iterator.traits] `_,`Updates to iterator_traits `_,"| indirectly_readable_traits | incrementable_traits",Christopher Di Bella,✅ -`[special.mem.concepts] `_,"| *no-throw-input-iterator* -| *no-throw-sentinel-for* -| *no-throw-input-range* -| *no-throw-forward-iterator* -| *no-throw-forward-range*","| [iterator.concepts] +`[special.mem.concepts] `_,"| *nothrow-input-iterator* +| *nothrow-sentinel-for* +| *nothrow-input-range* +| *nothrow-forward-iterator* +| *nothrow-forward-range*","| [iterator.concepts] | [range.refinements]",Konstantin Varlamov,✅ -`[specialized.algorithms] `_,"| ranges::uninitialized_default_construct -| ranges::uninitialized_default_construct_n +`[specialized.algorithms] `_,"| `ranges::uninitialized_default_construct ` +| `ranges::uninitialized_default_construct_n ` | ranges::uninitialized_value_construct | ranges::uninitialized_value_construct_n | ranges::uninitialized_copy @@ -31,7 +31,7 @@ | ranges::construct_at | ranges::destroy | ranges::destroy_at -| ranges::destroy_n",[special.mem.concepts],Konstantin Varlamov,Not started +| ranges::destroy_n",[special.mem.concepts],Konstantin Varlamov,In progress [strings],Adds begin/end and updates const_iterator.,[iterator.concepts],Unassigned,Not started [views.span],Same as [strings],[iterator.concepts],Unassigned,Not started `[iterator.cust.move] `_,`ranges::iter_move `_,,Zoe Carver,✅ diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -231,6 +231,7 @@ __memory/concepts.h __memory/construct_at.h __memory/pointer_traits.h + __memory/ranges_uninitialized_algorithms.h __memory/raw_storage_iterator.h __memory/shared_ptr.h __memory/temporary_buffer.h diff --git a/libcxx/include/__memory/ranges_uninitialized_algorithms.h b/libcxx/include/__memory/ranges_uninitialized_algorithms.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__memory/ranges_uninitialized_algorithms.h @@ -0,0 +1,90 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___MEMORY_RANGES_UNINITIALIZED_ALGORITHMS_H +#define _LIBCPP___MEMORY_RANGES_UNINITIALIZED_ALGORITHMS_H + +#include <__config> +#include <__function_like.h> +#include <__iterator/incrementable_traits.h> +#include <__iterator/iterator_traits.h> +#include <__iterator/readable_traits.h> +#include <__memory/concepts.h> +#include <__memory/uninitialized_algorithms.h> +#include <__ranges/access.h> +#include <__ranges/concepts.h> +#include <__ranges/dangling.h> +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if !defined(_LIBCPP_HAS_NO_RANGES) +namespace ranges { + +// uninitialized_default_construct + +struct __uninitialized_default_construct_fn final : private __function_like { + + constexpr explicit __uninitialized_default_construct_fn(__tag __x) noexcept : + __function_like(__x) {} + + template <__nothrow_forward_iterator _ForwardIterator, + __nothrow_sentinel_for<_ForwardIterator> _Sentinel> + requires default_initializable> + _ForwardIterator operator()(_ForwardIterator __first, _Sentinel __last) const { + using _ValueType = remove_reference_t>; + return _VSTD::__uninitialized_default_construct<_ValueType>(__first, __last); + } + + template <__nothrow_forward_range _ForwardRange> + requires default_initializable> + borrowed_iterator_t<_ForwardRange> operator()(_ForwardRange&& __range) const { + return (*this)(ranges::begin(__range), ranges::end(__range)); + } + +}; + +inline namespace __cpo { +inline constexpr auto uninitialized_default_construct = + __uninitialized_default_construct_fn(__function_like::__tag()); +} // namespace __cpo + +// uninitialized_default_construct_n + +struct __uninitialized_default_construct_n_fn final : private __function_like { + + constexpr explicit __uninitialized_default_construct_n_fn(__tag __x) noexcept : + __function_like(__x) {} + + template <__nothrow_forward_iterator _ForwardIterator> + requires default_initializable> + _ForwardIterator operator()(_ForwardIterator __first, + iter_difference_t<_ForwardIterator> __n) const { + using _ValueType = remove_reference_t>; + return _VSTD::__uninitialized_default_construct_n<_ValueType>(__first, __n); + } + +}; + +inline namespace __cpo { +inline constexpr auto uninitialized_default_construct_n = + __uninitialized_default_construct_n_fn(__function_like::__tag()); +} // namespace __cpo + +} // namespace ranges +#endif // !defined(_LIBCPP_HAS_NO_RANGES) + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___MEMORY_RANGES_UNINITIALIZED_ALGORITHMS_H diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h --- a/libcxx/include/__memory/uninitialized_algorithms.h +++ b/libcxx/include/__memory/uninitialized_algorithms.h @@ -119,43 +119,61 @@ #if _LIBCPP_STD_VER > 14 -template -inline _LIBCPP_INLINE_VISIBILITY -void uninitialized_default_construct(_ForwardIterator __first, _ForwardIterator __last) { - using _Vt = typename iterator_traits<_ForwardIterator>::value_type; +// uninitialized_default_construct + +template +inline _LIBCPP_HIDE_FROM_ABI +_ForwardIterator __uninitialized_default_construct(_ForwardIterator __first, _Sentinel __last) { auto __idx = __first; #ifndef _LIBCPP_NO_EXCEPTIONS try { #endif for (; __idx != __last; ++__idx) - ::new ((void*)_VSTD::addressof(*__idx)) _Vt; + ::new ((void*)_VSTD::addressof(*__idx)) _ValueType; #ifndef _LIBCPP_NO_EXCEPTIONS } catch (...) { _VSTD::destroy(__first, __idx); throw; } #endif + + return __idx; } -template +template inline _LIBCPP_INLINE_VISIBILITY -_ForwardIterator uninitialized_default_construct_n(_ForwardIterator __first, _Size __n) { - using _Vt = typename iterator_traits<_ForwardIterator>::value_type; +void uninitialized_default_construct(_ForwardIterator __first, _ForwardIterator __last) { + using _ValueType = typename iterator_traits<_ForwardIterator>::value_type; + (void)_VSTD::__uninitialized_default_construct<_ValueType>(__first, __last); +} + +// uninitialized_default_construct_n + +template +inline _LIBCPP_HIDE_FROM_ABI +_ForwardIterator __uninitialized_default_construct_n(_ForwardIterator __first, _Size __n) { auto __idx = __first; #ifndef _LIBCPP_NO_EXCEPTIONS try { #endif for (; __n > 0; ++__idx, (void) --__n) - ::new ((void*)_VSTD::addressof(*__idx)) _Vt; - return __idx; + ::new ((void*)_VSTD::addressof(*__idx)) _ValueType; #ifndef _LIBCPP_NO_EXCEPTIONS } catch (...) { _VSTD::destroy(__first, __idx); throw; } #endif + + return __idx; } +template +inline _LIBCPP_INLINE_VISIBILITY +_ForwardIterator uninitialized_default_construct_n(_ForwardIterator __first, _Size __n) { + using _ValueType = typename iterator_traits<_ForwardIterator>::value_type; + return _VSTD::__uninitialized_default_construct_n<_ValueType>(__first, __n); +} template inline _LIBCPP_INLINE_VISIBILITY diff --git a/libcxx/include/memory b/libcxx/include/memory --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -219,9 +219,21 @@ template void uninitialized_default_construct(ForwardIterator first, ForwardIterator last); +template Sentinel> + requires default_initializable> + ForwardIterator ranges::uninitialized_default_construct(ForwardIterator first, Sentinel last); // since C++20 + +template + requires default_initializable> + borrowed_iterator_t ranges::uninitialized_default_construct(ForwardRange&& r); // since C++20 + template ForwardIterator uninitialized_default_construct_n(ForwardIterator first, Size n); +template + requires default_initializable> + ForwardIterator ranges::uninitialized_default_construct_n(ForwardIterator first, iter_difference_t n); // since C++20 + template struct auto_ptr_ref {}; // deprecated in C++11, removed in C++17 template @@ -672,6 +684,7 @@ #include <__memory/concepts.h> #include <__memory/construct_at.h> #include <__memory/pointer_traits.h> +#include <__memory/ranges_uninitialized_algorithms.h> #include <__memory/raw_storage_iterator.h> #include <__memory/shared_ptr.h> #include <__memory/temporary_buffer.h> diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -630,22 +630,26 @@ export * module __memory { - module addressof { private header "__memory/addressof.h" } - module allocation_guard { private header "__memory/allocation_guard.h" } - module allocator { private header "__memory/allocator.h" } - module allocator_arg_t { private header "__memory/allocator_arg_t.h" } - module allocator_traits { private header "__memory/allocator_traits.h" } - module auto_ptr { private header "__memory/auto_ptr.h" } - module compressed_pair { private header "__memory/compressed_pair.h" } - module concepts { private header "__memory/concepts.h" } - module construct_at { private header "__memory/construct_at.h" } - module pointer_traits { private header "__memory/pointer_traits.h" } - module raw_storage_iterator { private header "__memory/raw_storage_iterator.h" } - module shared_ptr { private header "__memory/shared_ptr.h" } - module temporary_buffer { private header "__memory/temporary_buffer.h" } - module uninitialized_algorithms { private header "__memory/uninitialized_algorithms.h" } - module unique_ptr { private header "__memory/unique_ptr.h" } - module uses_allocator { private header "__memory/uses_allocator.h" } + module addressof { private header "__memory/addressof.h" } + module allocation_guard { private header "__memory/allocation_guard.h" } + module allocator { private header "__memory/allocator.h" } + module allocator_arg_t { private header "__memory/allocator_arg_t.h" } + module allocator_traits { private header "__memory/allocator_traits.h" } + module auto_ptr { private header "__memory/auto_ptr.h" } + module compressed_pair { private header "__memory/compressed_pair.h" } + module concepts { private header "__memory/concepts.h" } + module construct_at { private header "__memory/construct_at.h" } + module pointer_traits { private header "__memory/pointer_traits.h" } + module ranges_uninitialized_algorithms { + private header "__memory/ranges_uninitialized_algorithms.h" + export __function_like + } + module raw_storage_iterator { private header "__memory/raw_storage_iterator.h" } + module shared_ptr { private header "__memory/shared_ptr.h" } + module temporary_buffer { private header "__memory/temporary_buffer.h" } + module uninitialized_algorithms { private header "__memory/uninitialized_algorithms.h" } + module unique_ptr { private header "__memory/unique_ptr.h" } + module uses_allocator { private header "__memory/uses_allocator.h" } } } module mutex { diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/memory/ranges_uninitialized_algorithms.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/memory/ranges_uninitialized_algorithms.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/memory/ranges_uninitialized_algorithms.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__memory/ranges_uninitialized_algorithms.h'}} +#include <__memory/ranges_uninitialized_algorithms.h> diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/special_function.compile.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/special_function.compile.pass.cpp --- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/special_function.compile.pass.cpp +++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/special_function.compile.pass.cpp @@ -81,6 +81,7 @@ } } // namespace test +// TODO(varconst): simply check that `advance` is a variable and not a function. // When found by unqualified ([basic.lookup.unqual]) name lookup for the postfix-expression in a // function call ([expr.call]), they inhibit argument-dependent name lookup. void adl_inhibition() { diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/special_function.compile.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/special_function.compile.pass.cpp --- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/special_function.compile.pass.cpp +++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/special_function.compile.pass.cpp @@ -88,6 +88,7 @@ } } // namespace test +// TODO(varconst): simply check that `next` is a variable and not a function. // When found by unqualified ([basic.lookup.unqual]) name lookup for the postfix-expression in a // function call ([expr.call]), they inhibit argument-dependent name lookup. void adl_inhibition() { diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/special_function.compile.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/special_function.compile.pass.cpp --- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/special_function.compile.pass.cpp +++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/special_function.compile.pass.cpp @@ -83,6 +83,7 @@ } } // namespace test +// TODO(varconst): simply check that `prev` is a variable and not a function. // When found by unqualified ([basic.lookup.unqual]) name lookup for the postfix-expression in a // function call ([expr.call]), they inhibit argument-dependent name lookup. void adl_inhibition() { diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct.pass.cpp @@ -0,0 +1,228 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges + +// + +// template Sentinel> +// requires default_initializable> +// ForwardIterator ranges::uninitialized_default_construct(ForwardIterator first, Sentinel last); +// +// template +// requires default_initializable> +// borrowed_iterator_t ranges::uninitialized_default_construct(ForwardRange&& range); + +#include +#include +#include +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" + +struct Counted { + static int current_objects; + static int total_objects; + static int throw_on; + + explicit Counted() { + if (throw_on == total_objects) { + TEST_THROW(1); + } + + ++current_objects; + ++total_objects; + } + + ~Counted() { --current_objects; } + + static void reset() { + current_objects = total_objects = 0; + throw_on = -1; + } + + Counted(Counted const&) = delete; + friend void operator&(Counted) = delete; +}; +int Counted::current_objects = 0; +int Counted::total_objects = 0; +int Counted::throw_on = -1; + +template +struct Buffer { + alignas(T) char buffer[sizeof(T) * N] = {}; + + T* begin() { return reinterpret_cast(buffer); } + T* end() { return begin() + N; } + const T* cbegin() const { return reinterpret_cast(buffer); } + const T* cend() const { return cbegin() + N; } + constexpr int capacity() const { return N; } +}; + +// Because this is a variable and not a function, it's guaranteed that ADL won't be used. However, +// implementations are allowed to use a different mechanism to achieve this effect, so this check is +// libc++-specific. +LIBCPP_STATIC_ASSERT(std::is_class_v); + +struct NotDefaultCtrable { NotDefaultCtrable() = delete; }; +static_assert(!std::is_invocable_v); + +int main(int, char**) { + // An empty range -- no default constructors should be invoked. + { + Buffer buf; + + std::ranges::uninitialized_default_construct(buf.begin(), buf.begin()); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + + std::ranges::uninitialized_default_construct(std::ranges::empty_view()); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + + forward_iterator it(buf.begin()); + auto range = std::ranges::subrange(it, sentinel_wrapper>(it)); + std::ranges::uninitialized_default_construct(range.begin(), range.end()); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + + std::ranges::uninitialized_default_construct(range); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + } + + // A range containing several objects, (iter, sentinel) overload. + { + Buffer buf; + std::ranges::uninitialized_default_construct(buf.begin(), buf.end()); + assert(Counted::current_objects == buf.capacity()); + assert(Counted::total_objects == buf.capacity()); + + std::destroy(buf.begin(), buf.end()); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == buf.capacity()); + Counted::reset(); + } + + // A range containing several objects, (range) overload. + { + Buffer buf; + auto range = std::ranges::subrange(buf.begin(), buf.end()); + std::ranges::uninitialized_default_construct(range); + assert(Counted::current_objects == buf.capacity()); + assert(Counted::total_objects == buf.capacity()); + + std::destroy(buf.begin(), buf.end()); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == buf.capacity()); + Counted::reset(); + } + + // Using `counted_iterator`. + { + constexpr int N = 3; + Buffer buf; + + std::ranges::uninitialized_default_construct( + std::counted_iterator(buf.begin(), N), std::default_sentinel); + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + + std::destroy(buf.begin(), buf.begin() + N); + Counted::reset(); + } + + // Using `views::counted`. + { + constexpr int N = 3; + Buffer buf; + + std::ranges::uninitialized_default_construct(std::views::counted(buf.begin(), N)); + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + + std::destroy(buf.begin(), buf.begin() + N); + Counted::reset(); + } + + // Using `reverse_view`. + { + constexpr int N = 3; + Buffer buf; + + auto range = std::ranges::subrange(buf.begin(), buf.begin() + N); + std::ranges::uninitialized_default_construct(std::ranges::reverse_view(range)); + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + + std::destroy(buf.begin(), buf.begin() + N); + Counted::reset(); + } + + // An exception is thrown while objects are being created -- the existing objects should stay + // valid. (iterator, sentinel) overload. +#ifndef TEST_HAS_NO_EXCEPTIONS + { + Buffer buf; + + Counted::throw_on = 3; // When constructing the fourth object (counting from one). + try { + std::ranges::uninitialized_default_construct(buf.begin(), buf.end()); + } catch(...) {} + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 3); + std::destroy(buf.begin(), buf.begin() + Counted::total_objects); + Counted::reset(); + } + + // An exception is thrown while objects are being created -- the existing objects should stay + // valid. (range) overload. + { + Buffer buf; + + Counted::throw_on = 3; // When constructing the fourth object. + try { + auto range = std::ranges::subrange(buf.begin(), buf.end()); + std::ranges::uninitialized_default_construct(range); + } catch(...) {} + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 3); + std::destroy(buf.begin(), buf.begin() + Counted::total_objects); + Counted::reset(); + } +#endif // TEST_HAS_NO_EXCEPTIONS + + // Works with const iterators, (iter, sentinel) overload. + { + Buffer buf; + + std::ranges::uninitialized_default_construct(buf.cbegin(), buf.cend()); + assert(Counted::current_objects == buf.capacity()); + assert(Counted::total_objects == buf.capacity()); + std::destroy(buf.begin(), buf.end()); + Counted::reset(); + } + + // Works with const iterators, (range) overload. + { + Buffer buf; + auto range = std::ranges::subrange(buf.cbegin(), buf.cend()); + + std::ranges::uninitialized_default_construct(range); + assert(Counted::current_objects == buf.capacity()); + assert(Counted::total_objects == buf.capacity()); + std::destroy(buf.begin(), buf.end()); + Counted::reset(); + } + + return 0; +} diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct_n.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct_n.pass.cpp @@ -0,0 +1,129 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges + +// + +// template +// requires default_initializable> +// ForwardIterator ranges::uninitialized_default_construct_n(ForwardIterator first, +// iter_difference_t n); + +#include +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" + +struct Counted { + static int current_objects; + static int total_objects; + static int throw_on; + + explicit Counted() { + if (throw_on == total_objects) { + TEST_THROW(1); + } + + ++current_objects; + ++total_objects; + } + + ~Counted() { --current_objects; } + + static void reset() { + current_objects = total_objects = 0; + throw_on = -1; + } + + Counted(Counted const&) = delete; + friend void operator&(Counted) = delete; +}; +int Counted::current_objects = 0; +int Counted::total_objects = 0; +int Counted::throw_on = -1; + +template +struct Buffer { + alignas(T) char buffer[sizeof(T) * N] = {}; + + T* begin() { return reinterpret_cast(buffer); } + T* end() { return begin() + N; } + const T* cbegin() const { return reinterpret_cast(buffer); } + const T* cend() const { return cbegin() + N; } +}; + +// Because this is a variable and not a function, it's guaranteed that ADL won't be used. However, +// implementations are allowed to use a different mechanism to achieve this effect, so this check is +// libc++-specific. +LIBCPP_STATIC_ASSERT(std::is_class_v); + +struct NotDefaultCtrable { NotDefaultCtrable() = delete; }; +static_assert(!std::is_invocable_v); + +int main(int, char**) { + // An empty range -- no default constructors should be invoked. + { + Buffer buf; + + std::ranges::uninitialized_default_construct_n(buf.begin(), 0); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + } + + // A range containing several objects. + { + constexpr int N = 5; + Buffer buf; + + std::ranges::uninitialized_default_construct_n(buf.begin(), N); + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + + std::destroy(buf.begin(), buf.end()); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == N); + Counted::reset(); + } + + // An exception is thrown while objects are being created -- the existing objects should stay + // valid. +#ifndef TEST_HAS_NO_EXCEPTIONS + { + constexpr int N = 5; + Buffer buf; + + Counted::throw_on = 3; // When constructing the fourth object (counting from one). + try { + std::ranges::uninitialized_default_construct_n(buf.begin(), N); + } catch(...) {} + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 3); + std::destroy(buf.begin(), buf.begin() + Counted::total_objects); + Counted::reset(); + } +#endif // TEST_HAS_NO_EXCEPTIONS + + // Works with const iterators. + { + constexpr int N = 5; + Buffer buf; + + std::ranges::uninitialized_default_construct_n(buf.cbegin(), N); + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + std::destroy(buf.begin(), buf.end()); + Counted::reset(); + } + + return 0; +} diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct.pass.cpp @@ -23,7 +23,6 @@ struct Counted { static int count; static int constructed; - static void reset() { count = constructed = 0; } explicit Counted() { ++count; ++constructed; } Counted(Counted const&) { assert(false); } ~Counted() { --count; } @@ -37,7 +36,6 @@ static int count; static int constructed; static int throw_after; - static void reset() { throw_after = count = constructed = 0; } explicit ThrowsCounted() { ++constructed; if (throw_after > 0 && --throw_after == 0) { @@ -67,7 +65,7 @@ assert(false); } catch (...) {} assert(ThrowsCounted::count == 0); - assert(ThrowsCounted::constructed == 4); // forth construction throws + assert(ThrowsCounted::constructed == 4); // Fourth construction throws #endif } @@ -87,26 +85,9 @@ assert(Counted::count == 0); } -void test_value_initialized() -{ - using It = forward_iterator; - const int N = 5; - int pool[N] = {-1, -1, -1, -1, -1}; - int* p = pool; - std::uninitialized_default_construct(It(p), It(p+1)); - assert(pool[0] == -1); - assert(pool[1] == -1); - std::uninitialized_default_construct(It(p+1), It(p+N)); - assert(pool[1] == -1); - assert(pool[2] == -1); - assert(pool[3] == -1); - assert(pool[4] == -1); -} - int main(int, char**) { test_counted(); - test_value_initialized(); test_ctor_throws(); return 0; diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct_n.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct_n.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct_n.pass.cpp @@ -88,29 +88,9 @@ assert(Counted::count == 0); } -void test_value_initialized() -{ - using It = forward_iterator; - const int N = 5; - int pool[N] = {-1, -1, -1, -1, -1}; - int* p = pool; - auto e = std::uninitialized_default_construct_n(It(p), 1); - assert(e == It(p+1)); - assert(pool[0] == -1); - assert(pool[1] == -1); - e = std::uninitialized_default_construct_n(It(p+1), 4); - assert(e == It(p+N)); - assert(pool[1] == -1); - assert(pool[2] == -1); - assert(pool[3] == -1); - assert(pool[4] == -1); -} - - int main(int, char**) { test_counted(); - test_value_initialized(); test_ctor_throws(); return 0;