diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -1,5 +1,6 @@ set(files __algorithm/adjacent_find.h + __algorithm/algorithms_results.h __algorithm/all_of.h __algorithm/any_of.h __algorithm/binary_search.h diff --git a/libcxx/include/__algorithm/algorithms_results.h b/libcxx/include/__algorithm/algorithms_results.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__algorithm/algorithms_results.h @@ -0,0 +1,50 @@ +// -*- 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___ALGORITHM_ALGORITHMS_RESULTS_H +#define _LIBCPP___ALGORITHM_ALGORITHMS_RESULTS_H + +#include <__concepts/convertible_to.h> +#include <__config> +#include <__utility/move.h> + +#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 { + +template +struct in_out_result { + [[no_unique_address]] _InputIterator in; + [[no_unique_address]] _OutputIterator out; + + template + requires convertible_to && convertible_to + constexpr operator in_out_result<_InputIterator2, _OutputIterator2>() const & { + return {in, out}; + } + + template + requires convertible_to<_InputIterator, _InputIterator2> && convertible_to<_OutputIterator, _OutputIterator2> + constexpr operator in_out_result<_InputIterator2, _OutputIterator2>() && { + return {_VSTD::move(in), _VSTD::move(out)}; + } +}; + +} // namespace ranges +#endif // !defined(_LIBCPP_HAS_NO_RANGES) + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___ALGORITHM_ALGORITHMS_RESULTS_H diff --git a/libcxx/include/__memory/ranges_uninitialized_algorithms.h b/libcxx/include/__memory/ranges_uninitialized_algorithms.h --- a/libcxx/include/__memory/ranges_uninitialized_algorithms.h +++ b/libcxx/include/__memory/ranges_uninitialized_algorithms.h @@ -10,10 +10,13 @@ #ifndef _LIBCPP___MEMORY_RANGES_UNINITIALIZED_ALGORITHMS_H #define _LIBCPP___MEMORY_RANGES_UNINITIALIZED_ALGORITHMS_H +#include <__algorithm/algorithms_results.h> #include <__concepts/constructible.h> #include <__config> #include <__function_like.h> +#include <__iterator/concepts.h> #include <__iterator/incrementable_traits.h> +#include <__iterator/iter_move.h> #include <__iterator/iterator_traits.h> #include <__iterator/readable_traits.h> #include <__memory/concepts.h> @@ -173,7 +176,7 @@ }; -} // namespace __uninitialized_fil +} // namespace __uninitialized_fill inline namespace __cpo { inline constexpr auto uninitialized_fill = __uninitialized_fill::__fn(__function_like::__tag()); @@ -204,6 +207,167 @@ inline constexpr auto uninitialized_fill_n = __uninitialized_fill_n::__fn(__function_like::__tag()); } // namespace __cpo +// uninitialized_copy + +template +using uninitialized_copy_result = in_out_result<_InputIterator, _OutputIterator>; + +namespace __uninitialized_copy { + +struct __fn final : private __function_like { + + constexpr explicit __fn(__tag __x) noexcept : __function_like(__x) {} + + // clang-format off + template _Sentinel1, + __nothrow_forward_iterator _OutputIterator, + __nothrow_sentinel_for<_OutputIterator> _Sentinel2> + requires constructible_from, iter_reference_t<_InputIterator>> + uninitialized_copy_result<_InputIterator, _OutputIterator> operator()( + _InputIterator __ifirst, _Sentinel1 __ilast, _OutputIterator __ofirst, _Sentinel2 __olast) const { + // clang-format on + using _ValueType = remove_reference_t>; + + auto __result = _VSTD::__uninitialized_copy<_ValueType>(_VSTD::move(__ifirst), _VSTD::move(__ilast), + _VSTD::move(__ofirst), _VSTD::move(__olast)); + return {_VSTD::move(__result.first), _VSTD::move(__result.second)}; + } + + // clang-format off + template + requires constructible_from, range_reference_t<_InputRange>> + uninitialized_copy_result, borrowed_iterator_t<_OutputRange>> operator()( + _InputRange&& __in_range, _OutputRange&& __out_range) const { + // clang-format on + return (*this)(ranges::begin(__in_range), ranges::end(__in_range), ranges::begin(__out_range), + ranges::end(__out_range)); + } +}; + +} // namespace __uninitialized_copy + +inline namespace __cpo { +inline constexpr auto uninitialized_copy = __uninitialized_copy::__fn(__function_like::__tag()); +} // namespace __cpo + +// uninitialized_copy_n + +template +using uninitialized_copy_n_result = in_out_result<_InputIterator, _OutputIterator>; + +namespace __uninitialized_copy_n { + +struct __fn final : private __function_like { + + constexpr explicit __fn(__tag __x) noexcept : __function_like(__x) {} + + // clang-format off + template _Sentinel> + requires constructible_from, iter_reference_t<_InputIterator>> + uninitialized_copy_n_result<_InputIterator, _OutputIterator> operator()( + _InputIterator __ifirst, iter_difference_t<_InputIterator> __n, _OutputIterator __ofirst, + _Sentinel __olast) const { + // clang-format on + using _ValueType = remove_reference_t>; + + auto __result = _VSTD::__uninitialized_copy_n<_ValueType>(_VSTD::move(__ifirst), __n, _VSTD::move(__ofirst), + _VSTD::move(__olast)); + return {_VSTD::move(__result.first), _VSTD::move(__result.second)}; + } + +}; + +} // namespace __uninitialized_copy_n + +inline namespace __cpo { +inline constexpr auto uninitialized_copy_n = __uninitialized_copy_n::__fn(__function_like::__tag()); +} // namespace __cpo + +// uninitialized_move + +template +using uninitialized_move_result = in_out_result<_InputIterator, _OutputIterator>; + +namespace __uninitialized_move { + +struct __fn final : private __function_like { + + constexpr explicit __fn(__tag __x) noexcept : __function_like(__x) {} + + // clang-format off + template _Sentinel1, + __nothrow_forward_iterator _OutputIterator, + __nothrow_sentinel_for<_OutputIterator> _Sentinel2> + requires constructible_from, iter_reference_t<_InputIterator>> + uninitialized_move_result<_InputIterator, _OutputIterator> operator()( + _InputIterator __ifirst, _Sentinel1 __ilast, _OutputIterator __ofirst, _Sentinel2 __olast) const { + // clang-format on + using _ValueType = remove_reference_t>; + auto __move = [](auto&& __iter) -> decltype(auto) { return ranges::iter_move(__iter); }; + + auto __result = _VSTD::__uninitialized_move<_ValueType>(_VSTD::move(__ifirst), _VSTD::move(__ilast), + _VSTD::move(__ofirst), _VSTD::move(__olast), __move); + return {_VSTD::move(__result.first), _VSTD::move(__result.second)}; + } + + // clang-format off + template + requires constructible_from, range_reference_t<_InputRange>> + uninitialized_move_result, borrowed_iterator_t<_OutputRange>> operator()( + _InputRange&& __in_range, _OutputRange&& __out_range) const { + // clang-format on + return (*this)(ranges::begin(__in_range), ranges::end(__in_range), ranges::begin(__out_range), + ranges::end(__out_range)); + } + +}; + +} // namespace __uninitialized_move + +inline namespace __cpo { +inline constexpr auto uninitialized_move = __uninitialized_move::__fn(__function_like::__tag()); +} // namespace __cpo + +// uninitialized_move_n + +template +using uninitialized_move_n_result = in_out_result<_InputIterator, _OutputIterator>; + +namespace __uninitialized_move_n { + +struct __fn final : private __function_like { + + constexpr explicit __fn(__tag __x) noexcept : __function_like(__x) {} + + // clang-format off + template _Sentinel> + requires constructible_from, iter_reference_t<_InputIterator>> + uninitialized_move_n_result<_InputIterator, _OutputIterator> operator()( + _InputIterator __ifirst, iter_difference_t<_InputIterator> __n, _OutputIterator __ofirst, + _Sentinel __olast) const { + // clang-format on + using _ValueType = remove_reference_t>; + auto __move = [](auto& __iter) -> decltype(auto) { return ranges::iter_move(__iter); }; + + auto __result = _VSTD::__uninitialized_move_n<_ValueType>(_VSTD::move(__ifirst), __n, _VSTD::move(__ofirst), + _VSTD::move(__olast), __move); + return {_VSTD::move(__result.first), _VSTD::move(__result.second)}; + } + +}; + +} // namespace __uninitialized_move_n + +inline namespace __cpo { +inline constexpr auto uninitialized_move_n = __uninitialized_move_n::__fn(__function_like::__tag()); +} // namespace __cpo + } // namespace ranges #endif // !defined(_LIBCPP_HAS_NO_RANGES) 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 @@ -23,52 +23,73 @@ _LIBCPP_BEGIN_NAMESPACE_STD -template -_ForwardIterator -uninitialized_copy(_InputIterator __f, _InputIterator __l, _ForwardIterator __r) -{ - typedef typename iterator_traits<_ForwardIterator>::value_type value_type; +// This is a simplified version of C++20 `unreachable_sentinel` that doesn't use concepts and thus can be used in any +// language mode. +struct __unreachable_sentinel { + template + _LIBCPP_HIDE_FROM_ABI friend _LIBCPP_CONSTEXPR_AFTER_CXX11 bool operator==( + __unreachable_sentinel, const _Iter&) _NOEXCEPT { + return false; + } +}; + +// uninitialized_copy + +template +inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> +__uninitialized_copy(_InputIterator __ifirst, _Sentinel1 __ilast, _ForwardIterator __ofirst, _Sentinel2 __olast) { + _ForwardIterator __idx = __ofirst; #ifndef _LIBCPP_NO_EXCEPTIONS - _ForwardIterator __s = __r; - try - { + try { #endif - for (; __f != __l; ++__f, (void) ++__r) - ::new ((void*)_VSTD::addressof(*__r)) value_type(*__f); + for (; __ifirst != __ilast && !(__olast == __idx); ++__ifirst, (void)++__idx) + ::new (_VSTD::__voidify(*__idx)) _ValueType(*__ifirst); #ifndef _LIBCPP_NO_EXCEPTIONS - } - catch (...) - { - for (; __s != __r; ++__s) - __s->~value_type(); - throw; - } + } catch (...) { + _VSTD::__destroy(__ofirst, __idx); + throw; + } #endif - return __r; + + return {_VSTD::move(__ifirst), _VSTD::move(__idx)}; } -template -_ForwardIterator -uninitialized_copy_n(_InputIterator __f, _Size __n, _ForwardIterator __r) -{ - typedef typename iterator_traits<_ForwardIterator>::value_type value_type; +template +_ForwardIterator uninitialized_copy(_InputIterator __ifirst, _InputIterator __ilast, _ForwardIterator __ofirst) { + typedef typename iterator_traits<_ForwardIterator>::value_type _ValueType; + auto __result = _VSTD::__uninitialized_copy<_ValueType>(_VSTD::move(__ifirst), _VSTD::move(__ilast), + _VSTD::move(__ofirst), __unreachable_sentinel()); + return _VSTD::move(__result.second); +} + +// uninitialized_copy_n + +template +inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> +__uninitialized_copy_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofirst, _Sentinel __olast) { + _ForwardIterator __idx = __ofirst; #ifndef _LIBCPP_NO_EXCEPTIONS - _ForwardIterator __s = __r; - try - { + try { #endif - for (; __n > 0; ++__f, (void) ++__r, (void) --__n) - ::new ((void*)_VSTD::addressof(*__r)) value_type(*__f); + for (; __n > 0 && !(__olast == __ofirst); ++__ifirst, (void)++__idx, (void)--__n) + ::new (_VSTD::__voidify(*__idx)) _ValueType(*__ifirst); #ifndef _LIBCPP_NO_EXCEPTIONS - } - catch (...) - { - for (; __s != __r; ++__s) - __s->~value_type(); - throw; - } + } catch (...) { + _VSTD::__destroy(__ofirst, __idx); + throw; + } #endif - return __r; + + return {_VSTD::move(__ifirst), _VSTD::move(__idx)}; +} + +template +inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator uninitialized_copy_n(_InputIterator __ifirst, _Size __n, + _ForwardIterator __ofirst) { + typedef typename iterator_traits<_ForwardIterator>::value_type _ValueType; + auto __result = _VSTD::__uninitialized_copy_n<_ValueType>(_VSTD::move(__ifirst), __n, _VSTD::move(__ofirst), + __unreachable_sentinel()); + return _VSTD::move(__result.second); } // uninitialized_fill @@ -253,43 +274,70 @@ return __uninitialized_value_construct_n<_ValueType>(_VSTD::move(__first), __n); } -template -inline _LIBCPP_INLINE_VISIBILITY -_ForwardIt uninitialized_move(_InputIt __first, _InputIt __last, _ForwardIt __first_res) { - using _Vt = typename iterator_traits<_ForwardIt>::value_type; - auto __idx = __first_res; +// uninitialized_move + +template +inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> +__uninitialized_move(_InputIterator __ifirst, _Sentinel1 __ilast, _ForwardIterator __ofirst, _Sentinel2 __olast, + _Move __move) { + auto __idx = __ofirst; #ifndef _LIBCPP_NO_EXCEPTIONS - try { + try { #endif - for (; __first != __last; ++__idx, (void) ++__first) - ::new ((void*)_VSTD::addressof(*__idx)) _Vt(_VSTD::move(*__first)); - return __idx; -#ifndef _LIBCPP_NO_EXCEPTIONS - } catch (...) { - _VSTD::destroy(__first_res, __idx); - throw; + for (; __ifirst != __ilast && !(__olast == __ofirst); ++__idx, (void)++__ifirst) { + ::new (_VSTD::__voidify(*__idx)) _ValueType(__move(__ifirst)); } +#ifndef _LIBCPP_NO_EXCEPTIONS + } catch (...) { + _VSTD::__destroy(__ofirst, __idx); + throw; + } #endif + + return {_VSTD::move(__ifirst), _VSTD::move(__idx)}; +} + +template +inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator uninitialized_move(_InputIterator __ifirst, _InputIterator __ilast, + _ForwardIterator __ofirst) { + using _ValueType = typename iterator_traits<_ForwardIterator>::value_type; + auto __move = [](auto& __iter) -> decltype(auto) { return _VSTD::move(*__iter); }; + + auto __result = _VSTD::__uninitialized_move<_ValueType>(_VSTD::move(__ifirst), _VSTD::move(__ilast), + _VSTD::move(__ofirst), __unreachable_sentinel(), __move); + return _VSTD::move(__result.second); } -template -inline _LIBCPP_INLINE_VISIBILITY -pair<_InputIt, _ForwardIt> -uninitialized_move_n(_InputIt __first, _Size __n, _ForwardIt __first_res) { - using _Vt = typename iterator_traits<_ForwardIt>::value_type; - auto __idx = __first_res; +// uninitialized_move_n + +template +inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> +__uninitialized_move_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofirst, _Sentinel __olast, _Move __move) { + auto __idx = __ofirst; #ifndef _LIBCPP_NO_EXCEPTIONS - try { + try { #endif - for (; __n > 0; ++__idx, (void) ++__first, --__n) - ::new ((void*)_VSTD::addressof(*__idx)) _Vt(_VSTD::move(*__first)); - return {__first, __idx}; + for (; __n > 0 && !(__olast == __ofirst); ++__idx, (void)++__ifirst, --__n) + ::new (_VSTD::__voidify(*__idx)) _ValueType(__move(__ifirst)); #ifndef _LIBCPP_NO_EXCEPTIONS - } catch (...) { - _VSTD::destroy(__first_res, __idx); - throw; - } + } catch (...) { + _VSTD::__destroy(__ofirst, __idx); + throw; + } #endif + + return {_VSTD::move(__ifirst), _VSTD::move(__idx)}; +} + +template +inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> +uninitialized_move_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofirst) { + using _ValueType = typename iterator_traits<_ForwardIterator>::value_type; + auto __move = [](auto& __iter) -> decltype(auto) { return _VSTD::move(*__iter); }; + + return _VSTD::__uninitialized_move_n<_ValueType>(_VSTD::move(__ifirst), __n, _VSTD::move(__ofirst), + __unreachable_sentinel(), __move); } #endif // _LIBCPP_STD_VER > 14 diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -641,6 +641,12 @@ constexpr bool // constexpr in C++20 prev_permutation(BidirectionalIterator first, BidirectionalIterator last, Compare comp); +namespace ranges { + // [algorithms.results], algorithm result types + template + struct in_out_result; +} + } // std */ @@ -661,6 +667,7 @@ #include #include <__algorithm/adjacent_find.h> +#include <__algorithm/algorithms_results.h> #include <__algorithm/all_of.h> #include <__algorithm/any_of.h> #include <__algorithm/binary_search.h> diff --git a/libcxx/include/memory b/libcxx/include/memory --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -181,10 +181,28 @@ ForwardIterator uninitialized_copy(InputIterator first, InputIterator last, ForwardIterator result); +template +using ranges::uninitialized_copy_result = in_out_result; // since C++20 + +template Sentinel1, nothrow-forward-iterator OutputIterator, nothrow-sentinel-for Sentinel2> + requires constructible_from, iter_reference_t> +ranges::uninitialized_copy_result +ranges::uninitialized_copy(InputIterator ifirst, Sentinel1 ilast, OutputIterator ofirst, Sentinel2 olast); // since C++20 + +template + requires constructible_from, range_reference_t> +ranges::uninitialized_copy_result, borrowed_iterator_t> +ranges::uninitialized_copy(InputRange&& in_range, OutputRange&& out_range); // since C++20 + template ForwardIterator uninitialized_copy_n(InputIterator first, Size n, ForwardIterator result); +template Sentinel> + requires constructible_from, iter_reference_t> +ranges::uninitialized_copy_n_result +ranges::uninitialized_copy_n(InputIterator ifirst, iter_difference_t n, OutputIterator ofirst, Sentinel olast); // since C++20 + template void uninitialized_fill(ForwardIterator first, ForwardIterator last, const T& x); @@ -219,9 +237,27 @@ template ForwardIterator uninitialized_move(InputIterator first, InputIterator last, ForwardIterator result); +template +using ranges::uninitialized_move_result = in_out_result; // since C++20 + +template Sentinel1, nothrow-forward-iterator OutputIterator, nothrow-sentinel-for Sentinel2> + requires constructible_from, iter_rvalue_reference_t> +ranges::uninitialized_move_result +ranges::uninitialized_move(InputIterator ifirst, Sentinel1 ilast, OutputIterator ofirst, Sentinel2 olast); // since C++20 + +template + requires constructible_from, range_rvalue_reference_t> +ranges::uninitialized_move_result, borrowed_iterator_t> +ranges::uninitialized_move(InputRange&& in_range, OutputRange&& out_range); // since C++20 + template pair uninitialized_move_n(InputIterator first, Size n, ForwardIterator result); +template Sentinel> + requires constructible_from, iter_rvalue_reference_t> +ranges::uninitialized_move_n_result +ranges::uninitialized_move_n(InputIterator ifirst, iter_difference_t n, OutputIterator ofirst, Sentinel olast); // since C++20 + template void uninitialized_value_construct(ForwardIterator first, ForwardIterator last); diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -220,6 +220,7 @@ module __algorithm { module adjacent_find { private header "__algorithm/adjacent_find.h" } + module algorithm_results { private header "__algorithm/algorithms_results.h" } module all_of { private header "__algorithm/all_of.h" } module any_of { private header "__algorithm/any_of.h" } module binary_search { private header "__algorithm/binary_search.h" } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/counted.h b/libcxx/test/std/utilities/memory/specialized.algorithms/counted.h --- a/libcxx/test/std/utilities/memory/specialized.algorithms/counted.h +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/counted.h @@ -15,6 +15,8 @@ struct Counted { static int current_objects; static int total_objects; + static int total_copies; + static int total_moves; static int throw_on; int value; @@ -33,12 +35,28 @@ static void reset() { current_objects = total_objects = 0; + total_copies = total_moves = 0; throw_on = -1; } Counted(const Counted& rhs) : value(rhs.value) { check_throw(); increase_counters(); + ++total_copies; + } + + Counted(Counted&& rhs) : value(rhs.value) { + check_throw(); + increase_counters(); + ++total_moves; + } + + friend bool operator==(const Counted& l, const Counted& r) { + return l.value == r.value; + } + + friend bool operator!=(const Counted& l, const Counted& r) { + return !(l == r); } friend void operator&(Counted) = delete; @@ -57,6 +75,8 @@ }; int Counted::current_objects = 0; int Counted::total_objects = 0; +int Counted::total_copies = 0; +int Counted::total_moves = 0; int Counted::throw_on = -1; #endif // LIBCPP_TEST_STD_UTILITIES_MEMORY_SPECIALIZED_ALGORITHMS_COUNTED_H 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 --- 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 @@ -154,8 +154,7 @@ 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); + std::ranges::uninitialized_default_construct(buf); } catch(...) {} assert(Counted::current_objects == 0); assert(Counted::total_objects == 3); diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct.pass.cpp @@ -173,8 +173,7 @@ Counted::throw_on = 3; // When constructing the fourth object. try { - auto range = std::ranges::subrange(buf.begin(), buf.end()); - std::ranges::uninitialized_value_construct(range); + std::ranges::uninitialized_value_construct(buf); } catch (...) { } assert(Counted::current_objects == 0); diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy.pass.cpp @@ -0,0 +1,288 @@ +//===----------------------------------------------------------------------===// +// +// 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 S1, nothrow-forward-iterator O, nothrow-sentinel-for S2> +// requires constructible_from, iter_reference_t> +// uninitialized_copy_result ranges::uninitialized_copy(I ifirst, S1 ilast, O ofirst, S2 olast); // since C++20 +// +// template +// requires constructible_from, range_reference_t> +// uninitialized_copy_result, borrowed_iterator_t> ranges::uninitialized_copy(IR&& in_range, OR&& out_range); // since C++20 + +#include +#include +#include +#include +#include +#include + +#include "../buffer.h" +#include "../counted.h" +#include "test_macros.h" +#include "test_iterators.h" + +// 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); + +int main(int, char**) { + // An empty range -- no default constructors should be invoked. + { + Counted in[] = {Counted()}; + Buffer out; + Counted::reset(); + + auto result1 = std::ranges::uninitialized_copy(in, in, out.begin(), out.end()); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + assert(Counted::total_copies == 0); + assert(result1.in == in); + assert(result1.out == out.begin()); + + std::ranges::empty_view view; + auto result2 = std::ranges::uninitialized_copy(view, out); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + assert(Counted::total_copies == 0); + assert(result2.in == view.begin()); + assert(result2.out == out.begin()); + + forward_iterator it(in); + auto range = std::ranges::subrange(it, sentinel_wrapper>(it)); + auto result3 = std::ranges::uninitialized_copy(range.begin(), range.end(), out.begin(), out.end()); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + assert(Counted::total_copies == 0); + assert(result3.in == it); + assert(result3.out == out.begin()); + + auto result4 = std::ranges::uninitialized_copy(range, out); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + assert(Counted::total_copies == 0); + assert(result4.in == it); + assert(result4.out == out.begin()); + } + Counted::reset(); + + // A range containing several objects, (iter, sentinel) overload. + { + constexpr int N = 5; + Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + auto result = std::ranges::uninitialized_copy(in, in + N, out.begin(), out.end()); + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + assert(Counted::total_copies == N); + assert(Counted::total_moves == 0); + assert(std::equal(in, in + N, out.begin(), out.end())); + assert(result.in == in + N); + assert(result.out == out.end()); + + std::destroy(out.begin(), out.end()); + } + Counted::reset(); + + // A range containing several objects, (range) overload. + { + constexpr int N = 5; + Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + auto range = std::ranges::subrange(in, in + N); + auto result = std::ranges::uninitialized_copy(range, out); + + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + assert(Counted::total_copies == N); + assert(Counted::total_moves == 0); + assert(std::equal(in, in + N, out.begin(), out.end())); + + assert(result.in == in + N); + assert(result.out == out.end()); + + std::destroy(out.begin(), out.end()); + } + Counted::reset(); + + // Using `counted_iterator`. + { + constexpr int N = 3; + Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + std::counted_iterator iter(in, N); + auto result = std::ranges::uninitialized_copy(iter, std::default_sentinel, out.begin(), out.end()); + + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + assert(Counted::total_copies == N); + assert(Counted::total_moves == 0); + assert(std::equal(in, in + N, out.begin(), out.begin() + N)); + + assert(result.in == iter + N); + assert(result.out == out.begin() + N); + + std::destroy(out.begin(), out.begin() + N); + } + Counted::reset(); + + // Using `views::counted`. + { + constexpr int N = 3; + Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + auto view = std::views::counted(in, N); + auto result = std::ranges::uninitialized_copy(view, out); + + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + assert(Counted::total_copies == N); + assert(Counted::total_moves == 0); + assert(std::equal(in, in + N, out.begin(), out.begin() + N)); + + assert(result.in == view.begin() + N); + assert(result.out == out.begin() + N); + + std::destroy(out.begin(), out.begin() + N); + } + Counted::reset(); + + // Using `reverse_view`. + { + constexpr int N = 3; + Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + auto range = std::ranges::subrange(in, in + N); + auto view = std::ranges::reverse_view(range); + auto result = std::ranges::uninitialized_copy(view, out); + + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + assert(Counted::total_copies == N); + assert(Counted::total_moves == 0); + + Counted expected[N] = {Counted(3), Counted(2), Counted(1)}; + assert(std::equal(out.begin(), out.begin() + N, expected, expected + N)); + + assert(result.in == view.begin() + N); + assert(result.out == out.begin() + N); + + std::destroy(out.begin(), out.begin() + N); + } + Counted::reset(); + + // Any existing values should be overwritten by copy constructors. + { + constexpr int N = 5; + int in[N] = {1, 2, 3, 4, 5}; + int out[N] = {6, 7, 8, 9, 10}; + assert(!std::equal(in, in + N, in, out + N)); + + std::ranges::uninitialized_copy(in, in + 1, out, out + N); + assert(out[0] == 1); + assert(out[1] == 7); + + std::ranges::uninitialized_copy(in, in + N, out, out + N); + assert(std::equal(in, in + N, out, out + N)); + } + + // An exception is thrown while objects are being created -- the existing objects should stay + // valid. (iterator, sentinel) overload. +#ifndef TEST_HAS_NO_EXCEPTIONS + { + constexpr int N = 3; + Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + Counted::throw_on = N; // When constructing the fourth object. + try { + std::ranges::uninitialized_copy(in, in + 5, out.begin(), out.end()); + } catch (...) { + } + assert(Counted::current_objects == 0); + assert(Counted::total_objects == N); + assert(Counted::total_copies == N); + assert(Counted::total_moves == 0); + + std::destroy(out.begin(), out.begin() + N); + } + Counted::reset(); + + // An exception is thrown while objects are being created -- the existing objects should stay + // valid. (range) overload. + { + constexpr int N = 3; + Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + Counted::throw_on = N; // When constructing the fourth object. + try { + std::ranges::uninitialized_copy(in, out); + } catch (...) { + } + assert(Counted::current_objects == 0); + assert(Counted::total_objects == N); + assert(Counted::total_copies == N); + assert(Counted::total_moves == 0); + + std::destroy(out.begin(), out.begin() + N); + } + Counted::reset(); +#endif // TEST_HAS_NO_EXCEPTIONS + + // Works with const iterators, (iter, sentinel) overload. + { + constexpr int N = 5; + Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + std::ranges::uninitialized_copy(std::cbegin(in), std::cend(in), out.begin(), out.end()); + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + assert(std::equal(in, in + N, out.begin(), out.end())); + + std::destroy(out.begin(), out.end()); + } + Counted::reset(); + + // Works with const iterators, (range) overload. + { + constexpr int N = 5; + Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + auto range = std::ranges::subrange(std::cbegin(in), std::cend(in)); + std::ranges::uninitialized_copy(range, out); + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + assert(std::equal(in, in + N, out.begin(), out.end())); + + std::destroy(out.begin(), out.end()); + } + Counted::reset(); + + return 0; +} diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy_n.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy_n.pass.cpp @@ -0,0 +1,112 @@ +//===----------------------------------------------------------------------===// +// +// 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 S> +// requires constructible_from, iter_reference_t> +// uninitialized_copy_n_result uninitialized_copy_n(I ifirst, iter_difference_t n, O ofirst, S olast); // since C++20 + +#include +#include +#include +#include +#include +#include + +#include "../buffer.h" +#include "../counted.h" +#include "test_macros.h" +#include "test_iterators.h" + +// 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); + +int main(int, char**) { + // An empty range -- no default constructors should be invoked. + { + Counted in[] = {Counted()}; + Buffer out; + Counted::reset(); + + auto result = std::ranges::uninitialized_copy_n(in, 0, out.begin(), out.end()); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + assert(Counted::total_copies == 0); + assert(result.in == in); + assert(result.out == out.begin()); + } + Counted::reset(); + + // A range containing several objects. + { + constexpr int N = 5; + Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + auto result = std::ranges::uninitialized_copy_n(in, N, out.begin(), out.end()); + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + assert(Counted::total_copies == N); + assert(Counted::total_moves == 0); + assert(std::equal(in, in + N, out.begin(), out.end())); + assert(result.in == in + N); + assert(result.out == out.end()); + + std::destroy(out.begin(), out.end()); + } + 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 + { + constexpr int N = 3; + Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + Counted::throw_on = N; // When constructing the fourth object. + try { + std::ranges::uninitialized_copy_n(in, 5, out.begin(), out.end()); + } catch (...) { + } + assert(Counted::current_objects == 0); + assert(Counted::total_objects == N); + assert(Counted::total_copies == N); + assert(Counted::total_moves == 0); + + std::destroy(out.begin(), out.begin() + N); + } + Counted::reset(); + +#endif // TEST_HAS_NO_EXCEPTIONS + + // Works with const iterators. + { + constexpr int N = 5; + Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + std::ranges::uninitialized_copy_n(std::cbegin(in), N, out.begin(), out.end()); + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + assert(std::equal(in, in + N, out.begin(), out.end())); + + std::destroy(out.begin(), out.end()); + } + Counted::reset(); + + return 0; +} diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/ranges_uninitialized_fill.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/ranges_uninitialized_fill.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/ranges_uninitialized_fill.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/ranges_uninitialized_fill.pass.cpp @@ -187,8 +187,7 @@ Counted::throw_on = N; // When constructing the fourth object. try { - auto range = std::ranges::subrange(buf.begin(), buf.end()); - std::ranges::uninitialized_fill(range, x); + std::ranges::uninitialized_fill(buf, x); } catch (...) { } assert(Counted::current_objects == 0); diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp @@ -0,0 +1,333 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +#include +#include +#include +#include +#include +#include +#include + +#include "../buffer.h" +#include "../counted.h" +#include "test_macros.h" +#include "test_iterators.h" + +// 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); + +namespace adl { + +template +struct Iterator { + using value_type = T; + using difference_type = int; + using iterator_concept = std::input_iterator_tag; + + T* ptr = nullptr; + + Iterator() = default; + explicit Iterator(int* p) : ptr(p) {} + + T& operator*() const { return *ptr; } + + Iterator& operator++() { ++ptr; return *this; } + Iterator operator++(int) { + Iterator prev = *this; + ++ptr; + return prev; + } + + friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr == rhs.ptr; } +}; + +static int iter_move_invocations = 0; + +template +T&& iter_move(Iterator& iter) { + ++iter_move_invocations; + return std::move(*iter); +} + +} // namespace adl + +int main(int, char**) { + // An empty range -- no default constructors should be invoked. + { + Counted in[] = {Counted()}; + Buffer out; + Counted::reset(); + + auto result1 = std::ranges::uninitialized_move(in, in, out.begin(), out.end()); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + assert(Counted::total_copies == 0); + assert(result1.in == in); + assert(result1.out == out.begin()); + + std::ranges::empty_view view; + auto result2 = std::ranges::uninitialized_move(view, out); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + assert(Counted::total_copies == 0); + assert(result2.in == view.begin()); + assert(result2.out == out.begin()); + + forward_iterator it(in); + auto range = std::ranges::subrange(it, sentinel_wrapper>(it)); + auto result3 = std::ranges::uninitialized_move(range.begin(), range.end(), out.begin(), out.end()); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + assert(Counted::total_copies == 0); + assert(result3.in == it); + assert(result3.out == out.begin()); + + auto result4 = std::ranges::uninitialized_move(range, out); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + assert(Counted::total_copies == 0); + assert(result4.in == it); + assert(result4.out == out.begin()); + } + Counted::reset(); + + // A range containing several objects, (iter, sentinel) overload. + { + constexpr int N = 5; + Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + auto result = std::ranges::uninitialized_move(in, in + N, out.begin(), out.end()); + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + assert(Counted::total_moves == N); + assert(Counted::total_copies == 0); + assert(std::equal(in, in + N, out.begin(), out.end())); + assert(result.in == in + N); + assert(result.out == out.end()); + + std::destroy(out.begin(), out.end()); + } + Counted::reset(); + + // A range containing several objects, (range) overload. + { + constexpr int N = 5; + Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + auto range = std::ranges::subrange(in, in + N); + auto result = std::ranges::uninitialized_move(range, out); + + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + assert(Counted::total_moves == N); + assert(Counted::total_copies == 0); + assert(std::equal(in, in + N, out.begin(), out.end())); + + assert(result.in == in + N); + assert(result.out == out.end()); + + std::destroy(out.begin(), out.end()); + } + Counted::reset(); + + // Using `counted_iterator`. + { + constexpr int N = 3; + Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + std::counted_iterator iter(in, N); + auto result = std::ranges::uninitialized_move(iter, std::default_sentinel, out.begin(), out.end()); + + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + assert(Counted::total_moves == N); + assert(Counted::total_copies == 0); + assert(std::equal(in, in + N, out.begin(), out.begin() + N)); + + assert(result.in == iter + N); + assert(result.out == out.begin() + N); + + std::destroy(out.begin(), out.begin() + N); + } + Counted::reset(); + + // Using `views::counted`. + { + constexpr int N = 3; + Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + auto view = std::views::counted(in, N); + auto result = std::ranges::uninitialized_move(view, out); + + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + assert(Counted::total_moves == N); + assert(Counted::total_copies == 0); + assert(std::equal(in, in + N, out.begin(), out.begin() + N)); + + assert(result.in == view.begin() + N); + assert(result.out == out.begin() + N); + + std::destroy(out.begin(), out.begin() + N); + } + Counted::reset(); + + // Using `reverse_view`. + { + constexpr int N = 3; + Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + auto range = std::ranges::subrange(in, in + N); + auto view = std::ranges::reverse_view(range); + auto result = std::ranges::uninitialized_move(view, out); + + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + assert(Counted::total_moves == N); + assert(Counted::total_copies == 0); + + Counted expected[N] = {Counted(3), Counted(2), Counted(1)}; + assert(std::equal(out.begin(), out.begin() + N, expected, expected + N)); + + assert(result.in == view.begin() + N); + assert(result.out == out.begin() + N); + + std::destroy(out.begin(), out.begin() + N); + } + Counted::reset(); + + // Any existing values should be overwritten by move constructors. + { + constexpr int N = 5; + int in[N] = {1, 2, 3, 4, 5}; + int out[N] = {6, 7, 8, 9, 10}; + assert(!std::equal(in, in + N, in, out + N)); + + std::ranges::uninitialized_move(in, in + 1, out, out + N); + assert(out[0] == 1); + assert(out[1] == 7); + + std::ranges::uninitialized_move(in, in + N, out, out + N); + assert(std::equal(in, in + N, out, out + N)); + } + + // An exception is thrown while objects are being created -- the existing objects should stay + // valid. (iterator, sentinel) overload. +#ifndef TEST_HAS_NO_EXCEPTIONS + { + constexpr int N = 3; + Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + Counted::throw_on = N; // When constructing the fourth object. + try { + std::ranges::uninitialized_move(in, in + 5, out.begin(), out.end()); + } catch (...) { + } + assert(Counted::current_objects == 0); + assert(Counted::total_objects == N); + assert(Counted::total_moves == N); + assert(Counted::total_copies == 0); + + std::destroy(out.begin(), out.begin() + N); + } + Counted::reset(); + + // An exception is thrown while objects are being created -- the existing objects should stay + // valid. (range) overload. + { + constexpr int N = 3; + Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + Counted::throw_on = N; // When constructing the fourth object. + try { + std::ranges::uninitialized_move(in, out); + } catch (...) { + } + assert(Counted::current_objects == 0); + assert(Counted::total_objects == N); + assert(Counted::total_moves == N); + assert(Counted::total_copies == 0); + + std::destroy(out.begin(), out.begin() + N); + } + Counted::reset(); +#endif // TEST_HAS_NO_EXCEPTIONS + + // Works with const iterators, (iter, sentinel) overload. + { + constexpr int N = 5; + Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + + std::ranges::uninitialized_move(std::cbegin(in), std::cend(in), out.begin(), out.end()); + assert(Counted::current_objects == N * 2); + assert(Counted::total_objects == N * 2); + assert(std::equal(in, in + N, out.begin(), out.end())); + + std::destroy(out.begin(), out.end()); + } + Counted::reset(); + + // Works with const iterators, (range) overload. + { + constexpr int N = 5; + Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + + auto range = std::ranges::subrange(std::cbegin(in), std::cend(in)); + std::ranges::uninitialized_move(range, out); + assert(Counted::current_objects == N * 2); + assert(Counted::total_objects == N * 2); + assert(std::equal(in, in + N, out.begin(), out.end())); + + std::destroy(out.begin(), out.end()); + } + Counted::reset(); + + // `iter_move` is a customization point. + { + constexpr int N = 3; + int in[N] = {1, 2, 3}; + Buffer out; + adl::Iterator begin(in); + adl::Iterator end(in + N); + + std::ranges::uninitialized_move(begin, end, out.begin(), out.end()); + assert(adl::iter_move_invocations == 3); + adl::iter_move_invocations = 0; + + auto range = std::ranges::subrange(begin, end); + std::ranges::uninitialized_move(range, out); + assert(adl::iter_move_invocations == 3); + adl::iter_move_invocations = 0; + } + + return 0; +} diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp @@ -0,0 +1,113 @@ +//===----------------------------------------------------------------------===// +// +// 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 S> +// requires constructible_from, iter_reference_t> +// uninitialized_copy_n_result uninitialized_copy_n(I ifirst, iter_difference_t n, O ofirst, S olast); // since C++20 + +#include +#include +#include +#include +#include +#include + +#include "../buffer.h" +#include "../counted.h" +#include "test_macros.h" +#include "test_iterators.h" + +// 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); + +int main(int, char**) { + // An empty range -- no default constructors should be invoked. + { + Counted in[] = {Counted()}; + Buffer out; + Counted::reset(); + + auto result = std::ranges::uninitialized_move_n(in, 0, out.begin(), out.end()); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + assert(Counted::total_copies == 0); + assert(result.in == in); + assert(result.out == out.begin()); + } + Counted::reset(); + + // A range containing several objects. + { + constexpr int N = 5; + Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + auto result = std::ranges::uninitialized_move_n(in, N, out.begin(), out.end()); + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + assert(Counted::total_moves == N); + assert(Counted::total_copies == 0); + assert(std::equal(in, in + N, out.begin(), out.end())); + assert(result.in == in + N); + assert(result.out == out.end()); + + std::destroy(out.begin(), out.end()); + } + 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 + { + constexpr int N = 3; + Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + Counted::throw_on = N; // When constructing the fourth object. + try { + std::ranges::uninitialized_move_n(in, 5, out.begin(), out.end()); + } catch (...) { + } + assert(Counted::current_objects == 0); + assert(Counted::total_objects == N); + assert(Counted::total_moves == N); + assert(Counted::total_copies == 0); + + std::destroy(out.begin(), out.begin() + N); + } + Counted::reset(); + +#endif // TEST_HAS_NO_EXCEPTIONS + + // Works with const iterators. + { + constexpr int N = 5; + Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + std::ranges::uninitialized_move_n(std::cbegin(in), N, out.begin(), out.end()); + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + assert(std::equal(in, in + N, out.begin(), out.end())); + + std::destroy(out.begin(), out.end()); + } + Counted::reset(); + + return 0; +} +