diff --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv --- a/libcxx/docs/Status/RangesAlgorithms.csv +++ b/libcxx/docs/Status/RangesAlgorithms.csv @@ -84,12 +84,12 @@ Permutation,sort_heap,Not assigned,n/a,Not started Permutation,prev_permutation,Not assigned,n/a,Not started Permutation,next_permutation,Not assigned,n/a,Not started -Uninitialised memory,uninitialized_copy,Konstantin Varlamov,n/a,Not started -Uninitialised memory,uninitialized_copy_n,Konstantin Varlamov,n/a,Not started +Uninitialised memory,uninitialized_copy,Konstantin Varlamov,`D116023 `_,✅ +Uninitialised memory,uninitialized_copy_n,Konstantin Varlamov,`D116023 `_,✅ Uninitialised memory,uninitialized_fill,Konstantin Varlamov,`D115626 `_,✅ Uninitialised memory,uninitialized_fill_n,Konstantin Varlamov,`D115626 `_,✅ -Uninitialised memory,uninitialized_move,Konstantin Varlamov,n/a,Not started -Uninitialised memory,uninitialized_move_n,Konstantin Varlamov,n/a,Not started +Uninitialised memory,uninitialized_move,Konstantin Varlamov,`D116023 `_,✅ +Uninitialised memory,uninitialized_move_n,Konstantin Varlamov,`D116023 `_,✅ Uninitialised memory,uninitialized_default_construct,Konstantin Varlamov,`D115315 `_,✅ Uninitialised memory,uninitialized_default_construct_n,Konstantin Varlamov,`D115315 `_,✅ Uninitialised memory,uninitialized_value_construct,Konstantin Varlamov,`D115626 `_,✅ 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 @@ -22,10 +22,10 @@ | `ranges::uninitialized_default_construct_n `_ | `ranges::uninitialized_value_construct `_ | `ranges::uninitialized_value_construct_n `_ -| ranges::uninitialized_copy -| ranges::uninitialized_copy_n -| ranges::uninitialized_move -| ranges::uninitialized_move_n +| `ranges::uninitialized_copy `_ +| `ranges::uninitialized_copy_n `_ +| `ranges::uninitialized_move `_ +| `ranges::uninitialized_move_n `_ | `ranges::uninitialized_fill `_ | `ranges::uninitialized_fill_n `_ | ranges::construct_at diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -26,6 +26,7 @@ __algorithm/generate_n.h __algorithm/generate.h __algorithm/half_positive.h + __algorithm/in_out_result.h __algorithm/includes.h __algorithm/inplace_merge.h __algorithm/is_heap_until.h diff --git a/libcxx/include/__algorithm/in_out_result.h b/libcxx/include/__algorithm/in_out_result.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__algorithm/in_out_result.h @@ -0,0 +1,52 @@ +// -*- 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_IN_OUT_RESULT_H +#define _LIBCPP___ALGORITHM_IN_OUT_RESULT_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 + _LIBCPP_HIDE_FROM_ABI + constexpr operator in_out_result<_InputIterator2, _OutputIterator2>() const & { + return {in, out}; + } + + template + requires convertible_to<_InputIterator, _InputIterator2> && convertible_to<_OutputIterator, _OutputIterator2> + _LIBCPP_HIDE_FROM_ABI + 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_IN_OUT_RESULT_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/in_out_result.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 __iter_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), __iter_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 __iter_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), __iter_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,72 @@ _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 bool operator!=(const _Iter&, __unreachable_sentinel) _NOEXCEPT { + return true; + } +}; + +// 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 && __idx != __olast; ++__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 && __idx != __olast; ++__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 +273,71 @@ 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, + _IterMove __iter_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 && __idx != __olast; ++__idx, (void)++__ifirst) { + ::new (_VSTD::__voidify(*__idx)) _ValueType(__iter_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 __iter_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(), __iter_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, + _IterMove __iter_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 && __idx != __olast; ++__idx, (void)++__ifirst, --__n) + ::new (_VSTD::__voidify(*__idx)) _ValueType(__iter_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 __iter_move = [](auto&& __iter) -> decltype(auto) { return _VSTD::move(*__iter); }; + + return _VSTD::__uninitialized_move_n<_ValueType>(_VSTD::move(__ifirst), __n, _VSTD::move(__ofirst), + __unreachable_sentinel(), __iter_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 */ @@ -687,6 +693,7 @@ #include <__algorithm/generate_n.h> #include <__algorithm/generate.h> #include <__algorithm/half_positive.h> +#include <__algorithm/in_out_result.h> #include <__algorithm/includes.h> #include <__algorithm/inplace_merge.h> #include <__algorithm/is_heap.h> diff --git a/libcxx/include/memory b/libcxx/include/memory --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -181,28 +181,65 @@ ForwardIterator uninitialized_copy(InputIterator first, InputIterator last, ForwardIterator result); +namespace ranges { + +template +using 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> +uninitialized_copy_result +uninitialized_copy(InputIterator ifirst, Sentinel1 ilast, OutputIterator ofirst, Sentinel2 olast); // since C++20 + +template + requires constructible_from, range_reference_t> +uninitialized_copy_result, borrowed_iterator_t> +uninitialized_copy(InputRange&& in_range, OutputRange&& out_range); // since C++20 + +} + template ForwardIterator uninitialized_copy_n(InputIterator first, Size n, ForwardIterator result); +namespace ranges { + +template +using uninitialized_copy_n_result = in_out_result; // since C++20 + +template Sentinel> + requires constructible_from, iter_reference_t> +uninitialized_copy_n_result +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); +namespace ranges { + template Sentinel, class T> requires constructible_from, const T&> -ForwardIterator ranges::uninitialized_fill(ForwardIterator first, Sentinel last, const T& x); // since C++20 +ForwardIterator uninitialized_fill(ForwardIterator first, Sentinel last, const T& x); // since C++20 template requires constructible_from, const T&> -borrowed_iterator_t ranges::uninitialized_fill(ForwardRange&& range, const T& x); // since C++20 +borrowed_iterator_t uninitialized_fill(ForwardRange&& range, const T& x); // since C++20 + +} template ForwardIterator uninitialized_fill_n(ForwardIterator first, Size n, const T& x); +namespace ranges { + template requires constructible_from, const T&> -ForwardIterator ranges::uninitialized_fill_n(ForwardIterator first, iter_difference_t n); // since C++20 +ForwardIterator uninitialized_fill_n(ForwardIterator first, iter_difference_t n); // since C++20 + +} template constexpr T* construct_at(T* location, Args&& ...args); // since C++20 @@ -219,44 +256,89 @@ template ForwardIterator uninitialized_move(InputIterator first, InputIterator last, ForwardIterator result); +namespace ranges { + +template +using 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> +uninitialized_move_result +uninitialized_move(InputIterator ifirst, Sentinel1 ilast, OutputIterator ofirst, Sentinel2 olast); // since C++20 + +template + requires constructible_from, range_rvalue_reference_t> +uninitialized_move_result, borrowed_iterator_t> +uninitialized_move(InputRange&& in_range, OutputRange&& out_range); // since C++20 + +} + template pair uninitialized_move_n(InputIterator first, Size n, ForwardIterator result); +namespace ranges { + +template +using uninitialized_move_n_result = in_out_result; // since C++20 + +template Sentinel> + requires constructible_from, iter_rvalue_reference_t> +uninitialized_move_n_result +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); +namespace ranges { + template Sentinel> requires default_initializable> - ForwardIterator ranges::uninitialized_value_construct(ForwardIterator first, Sentinel last); // since C++20 + ForwardIterator uninitialized_value_construct(ForwardIterator first, Sentinel last); // since C++20 template requires default_initializable> - borrowed_iterator_t ranges::uninitialized_value_construct(ForwardRange&& r); // since C++20 + borrowed_iterator_t uninitialized_value_construct(ForwardRange&& r); // since C++20 + +} template ForwardIterator uninitialized_value_construct_n(ForwardIterator first, Size n); +namespace ranges { + template requires default_initializable> - ForwardIterator ranges::uninitialized_value_construct_n(ForwardIterator first, iter_difference_t n); // since C++20 + ForwardIterator uninitialized_value_construct_n(ForwardIterator first, iter_difference_t n); // since C++20 + +} template void uninitialized_default_construct(ForwardIterator first, ForwardIterator last); +namespace ranges { + template Sentinel> requires default_initializable> - ForwardIterator ranges::uninitialized_default_construct(ForwardIterator first, Sentinel last); // since C++20 + ForwardIterator 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 + borrowed_iterator_t uninitialized_default_construct(ForwardRange&& r); // since C++20 + +} template ForwardIterator uninitialized_default_construct_n(ForwardIterator first, Size n); +namespace ranges { + template requires default_initializable> - ForwardIterator ranges::uninitialized_default_construct_n(ForwardIterator first, iter_difference_t n); // since C++20 + ForwardIterator 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 diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -246,6 +246,7 @@ module generate { private header "__algorithm/generate.h" } module generate_n { private header "__algorithm/generate_n.h" } module half_positive { private header "__algorithm/half_positive.h" } + module in_out_result { private header "__algorithm/in_out_result.h" } module includes { private header "__algorithm/includes.h" } module inplace_merge { private header "__algorithm/inplace_merge.h" } module is_heap { private header "__algorithm/is_heap.h" } diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/algorithm/in_out_result.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/algorithm/in_out_result.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/algorithm/in_out_result.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: '__algorithm/in_out_result.h'}} +#include <__algorithm/in_out_result.h> diff --git a/libcxx/test/std/algorithms/algorithms.results/in_out_result.pass.cpp b/libcxx/test/std/algorithms/algorithms.results/in_out_result.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/algorithms.results/in_out_result.pass.cpp @@ -0,0 +1,114 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// +// +// namespace ranges { +// template +// struct in_out_result; +// } + +#include +#include +#include + +// Size optimization. +struct Empty {}; +struct Empty2 {}; +static_assert(sizeof(std::ranges::in_out_result) == sizeof(int)); +static_assert(sizeof(std::ranges::in_out_result) == sizeof(int)); +static_assert(sizeof(std::ranges::in_out_result) == sizeof(char)); + +int main(int, char**) { + // Conversion, fundamental types. + { + std::ranges::in_out_result x = {2, false}; + std::ranges::in_out_result y = x; + assert(y.in == 2.0); + assert(y.out == '\0'); + } + + // Conversion, user-defined types. + { + struct From1 { + int value = 0; + From1(int v) : value(v) {} + }; + + struct To1 { + int value = 0; + To1(int v) : value(v) {} + + To1(const From1& f) : value(f.value) {}; + }; + + struct To2 { + int value = 0; + To2(int v) : value(v) {} + }; + struct From2 { + int value = 0; + From2(int v) : value(v) {} + + operator To2() const { return To2(value); } + }; + + + std::ranges::in_out_result x = {42, 99}; + std::ranges::in_out_result y = x; + assert(y.in.value == 42); + assert(y.out.value == 99); + } + + // Copy-only type. + { + struct CopyOnly { + int value = 0; + CopyOnly(int v) : value(v) {} + + CopyOnly(const CopyOnly&) = default; + CopyOnly(CopyOnly&&) = delete; + }; + + std::ranges::in_out_result x = {42, 99}; + auto y = x; + assert(y.in.value == 42); + assert(y.out.value == 99); + } + + // Move-only type. + { + struct MoveOnly { + int value = 0; + MoveOnly(int v) : value(v) {} + + MoveOnly(MoveOnly&&) = default; + MoveOnly(const MoveOnly&) = delete; + }; + + std::ranges::in_out_result x = {42, 99}; + auto y = std::move(x); + assert(y.in.value == 42); + assert(y.out.value == 99); + } + + // Unsuccessful conversion. + { + struct Foo1 {}; + struct Foo2 {}; + struct Bar1 {}; + struct Bar2 {}; + static_assert( + !std::is_convertible_v, std::ranges::in_out_result>); + } + + return 0; +} 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,9 +15,12 @@ struct Counted { static int current_objects; static int total_objects; + static int total_copies; + static int total_moves; static int throw_on; int value; + bool moved_from = false; explicit Counted() { check_throw(); @@ -33,12 +36,30 @@ 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(); + + rhs.moved_from = true; + ++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 +78,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 @@ -30,6 +30,7 @@ #include "test_macros.h" #include "test_iterators.h" +// TODO(varconst): consolidate the ADL checks into a single file. // 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. @@ -154,8 +155,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.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 --- 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 @@ -25,6 +25,7 @@ #include "test_macros.h" #include "test_iterators.h" +// TODO(varconst): consolidate the ADL checks into a single file. // 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. 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 @@ -66,7 +66,7 @@ assert(false); } catch (...) {} assert(ThrowsCounted::count == 0); - assert(ThrowsCounted::constructed == 4); // forth construction throws + assert(ThrowsCounted::constructed == 4); // Fourth construction throws #endif } 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 @@ -30,6 +30,7 @@ #include "test_macros.h" #include "test_iterators.h" +// TODO(varconst): consolidate the ADL checks into a single file. // 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. @@ -173,8 +174,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.construct.value/ranges_uninitialized_value_construct_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct_n.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct_n.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct_n.pass.cpp @@ -25,6 +25,7 @@ #include "test_macros.h" #include "test_iterators.h" +// TODO(varconst): consolidate the ADL checks into a single file. // 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. 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,374 @@ +//===----------------------------------------------------------------------===// +// +// 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" + +// TODO(varconst): consolidate the ADL checks into a single file. +// 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); + +static_assert(std::is_invocable_v); +struct NotConvertibleFromInt {}; +static_assert(!std::is_invocable_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(in, in, 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()); + } + + { + std::ranges::empty_view view; + auto result = std::ranges::uninitialized_copy(view, out); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + assert(Counted::total_copies == 0); + assert(result.in == view.begin()); + assert(result.out == out.begin()); + } + + { + forward_iterator it(in); + std::ranges::subrange range(it, sentinel_wrapper>(it)); + + auto result = 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(result.in == it); + assert(result.out == out.begin()); + } + + { + forward_iterator it(in); + std::ranges::subrange range(it, sentinel_wrapper>(it)); + + auto result = std::ranges::uninitialized_copy(range, out); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + assert(Counted::total_copies == 0); + assert(result.in == it); + assert(result.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_SAME_TYPE(decltype(result), std::ranges::uninitialized_copy_result); + + 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(); + + std::ranges::subrange range(in, in + N); + auto result = std::ranges::uninitialized_copy(range, out); + ASSERT_SAME_TYPE(decltype(result), std::ranges::uninitialized_copy_result); + + 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(); + + std::ranges::subrange range(in, in + N); + auto view = std::ranges::views::reverse(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 -- objects not yet overwritten should + // stay valid. (iterator, sentinel) overload. +#ifndef TEST_HAS_NO_EXCEPTIONS + { + constexpr int M = 3; + constexpr int N = 5; + Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Counted out[N] = {Counted(6), Counted(7), Counted(8), Counted(9), Counted(10)}; + Counted::reset(); + + Counted::throw_on = M; // When constructing out[3]. + try { + std::ranges::uninitialized_copy(in, in + N, out, out + N); + assert(false); + } catch (...) { + } + assert(Counted::current_objects == 0); + assert(Counted::total_objects == M); + assert(Counted::total_copies == M); + assert(Counted::total_moves == 0); + + assert(out[4].value == 10); + } + Counted::reset(); + + // An exception is thrown while objects are being created -- objects not yet overwritten should + // stay valid. (range) overload. + { + constexpr int M = 3; + constexpr int N = 5; + Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Counted out[N] = {Counted(6), Counted(7), Counted(8), Counted(9), Counted(10)}; + Counted::reset(); + + Counted::throw_on = M; // When constructing out[3]. + try { + std::ranges::uninitialized_copy(in, out); + assert(false); + } catch (...) { + } + assert(Counted::current_objects == 0); + assert(Counted::total_objects == M); + assert(Counted::total_copies == M); + assert(Counted::total_moves == 0); + + assert(out[4].value == 10); + } + 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(in, in + N, out.cbegin(), out.cend()); + 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(); + + std::ranges::subrange out_range(out.cbegin(), out.cend()); + std::ranges::uninitialized_copy(in, out_range); + 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(); + + // Conversions, (iter, sentinel) overload. + { + constexpr int N = 3; + double in[N] = {1.0, 2.0, 3.0}; + Buffer out; + + std::ranges::uninitialized_copy(in, in + N, out.begin(), out.end()); + assert(std::equal(in, in + N, out.begin(), out.end())); + } + + // Conversions, (range) overload. + { + constexpr int N = 3; + double in[N] = {1.0, 2.0, 3.0}; + Buffer out; + + std::ranges::uninitialized_copy(in, out); + assert(std::equal(in, in + N, out.begin(), out.end())); + } + + // Destination range is shorter than the source range, (iter, sentinel) overload. + { + constexpr int M = 3; + 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 == M); + assert(Counted::total_objects == M); + assert(Counted::total_copies == M); + assert(Counted::total_moves == 0); + + assert(std::equal(in, in + M, out.begin(), out.end())); + assert(result.in == in + M); + assert(result.out == out.end()); + } + + // Destination range is shorter than the source range, (range) overload. + { + constexpr int M = 3; + constexpr int N = 5; + Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + std::ranges::subrange range(in, in + N); + auto result = std::ranges::uninitialized_copy(range, out); + assert(Counted::current_objects == M); + assert(Counted::total_objects == M); + assert(Counted::total_copies == M); + assert(Counted::total_moves == 0); + + assert(std::equal(in, in + M, out.begin(), out.end())); + assert(result.in == in + M); + assert(result.out == out.end()); + } + + 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,152 @@ +//===----------------------------------------------------------------------===// +// +// 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" + +// TODO(varconst): consolidate the ADL checks into a single file. +// 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); + +static_assert(std::is_invocable_v); +struct NotConvertibleFromInt {}; +static_assert(!std::is_invocable_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_SAME_TYPE(decltype(result), std::ranges::uninitialized_copy_n_result); + + 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 M = 3; + constexpr int N = 5; + Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Counted out[N] = {Counted(6), Counted(7), Counted(8), Counted(9), Counted(10)}; + Counted::reset(); + + Counted::throw_on = M; // When constructing out[3]. + try { + std::ranges::uninitialized_copy_n(in, N, out, out + N); + assert(false); + } catch (...) { + } + assert(Counted::current_objects == 0); + assert(Counted::total_objects == M); + assert(Counted::total_copies == M); + assert(Counted::total_moves == 0); + + assert(out[4].value == 10); + } + 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(in, N, out.cbegin(), out.cend()); + 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(); + + // Conversions. + { + constexpr int N = 3; + double in[N] = {1.0, 2.0, 3.0}; + Buffer out; + + std::ranges::uninitialized_copy_n(in, N, out.begin(), out.end()); + assert(std::equal(in, in + N, out.begin(), out.end())); + } + + // Destination range is shorter than the source range. + { + constexpr int M = 3; + 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 == M); + assert(Counted::total_objects == M); + assert(Counted::total_copies == M); + assert(Counted::total_moves == 0); + + assert(std::equal(in, in + M, out.begin(), out.end())); + assert(result.in == in + M); + assert(result.out == out.end()); + } + + return 0; +} diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/ranges_uninitialized_fill_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/ranges_uninitialized_fill_n.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/ranges_uninitialized_fill_n.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/ranges_uninitialized_fill_n.pass.cpp @@ -27,14 +27,14 @@ #include "test_macros.h" #include "test_iterators.h" +// TODO(varconst): consolidate the ADL checks into a single file. // 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 NotConvertibleFromInt {}; -static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); int main(int, char**) { constexpr int value = 42; 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 @@ -31,6 +31,7 @@ #include "test_macros.h" #include "test_iterators.h" +// TODO(varconst): consolidate the ADL checks into a single file. // 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. @@ -187,8 +188,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,428 @@ +//===----------------------------------------------------------------------===// +// +// 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 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 + + +#include +#include +#include +#include +#include +#include +#include + +#include "../buffer.h" +#include "../counted.h" +#include "test_macros.h" +#include "test_iterators.h" + +// TODO(varconst): consolidate the ADL checks into a single file. +// 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); + +static_assert(std::is_invocable_v); +struct NotConvertibleFromInt {}; +static_assert(!std::is_invocable_v); + +namespace adl { + +static int iter_move_invocations = 0; + +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 T&& iter_move(Iterator iter) { + ++iter_move_invocations; + return std::move(*iter); + } + + friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr == rhs.ptr; } +}; + +} // namespace adl + +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(in, in, 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()); + } + + { + std::ranges::empty_view view; + auto result = std::ranges::uninitialized_move(view, out); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + assert(Counted::total_copies == 0); + assert(result.in == view.begin()); + assert(result.out == out.begin()); + } + + { + forward_iterator it(in); + std::ranges::subrange range(it, sentinel_wrapper>(it)); + + auto result = 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(result.in == it); + assert(result.out == out.begin()); + } + + { + forward_iterator it(in); + std::ranges::subrange range(it, sentinel_wrapper>(it)); + + auto result = std::ranges::uninitialized_move(range, out); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + assert(Counted::total_copies == 0); + assert(result.in == it); + assert(result.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_SAME_TYPE(decltype(result), std::ranges::uninitialized_move_result); + 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(); + + std::ranges::subrange range(in, in + N); + auto result = std::ranges::uninitialized_move(range, out); + ASSERT_SAME_TYPE(decltype(result), std::ranges::uninitialized_move_result); + + 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(); + + std::ranges::subrange range(in, in + N); + auto view = std::ranges::views::reverse(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 -- check that the objects in the source + // range have been moved from. (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 out[3]. + try { + std::ranges::uninitialized_move(in, in + 5, out.begin(), out.end()); + assert(false); + } catch (...) { + } + assert(Counted::current_objects == 0); + assert(Counted::total_objects == N); + assert(Counted::total_moves == N); + assert(Counted::total_copies == 0); + + assert(std::all_of(in, in + 1, [](const auto& e) { return e.moved_from; })); + assert(std::none_of(in + N, in + 5, [](const auto& e) { return e.moved_from; })); + + std::destroy(out.begin(), out.begin() + N); + } + Counted::reset(); + + // An exception is thrown while objects are being created -- check that the objects in the source + // range have been moved from. (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 out[3]. + try { + std::ranges::uninitialized_move(in, out); + assert(false); + } catch (...) { + } + assert(Counted::current_objects == 0); + assert(Counted::total_objects == N); + assert(Counted::total_moves == N); + assert(Counted::total_copies == 0); + + assert(std::all_of(in, in + 1, [](const auto& e) { return e.moved_from; })); + assert(std::none_of(in + N, in + 5, [](const auto& e) { return e.moved_from; })); + + 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_move(in, in + N, out.cbegin(), out.cend()); + 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(); + + std::ranges::subrange out_range (out.cbegin(), out.cend()); + std::ranges::uninitialized_move(in, out_range); + 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(); + + // Conversions, (iter, sentinel) overload. + { + constexpr int N = 3; + double in[N] = {1.0, 2.0, 3.0}; + Buffer out; + + std::ranges::uninitialized_move(in, in + N, out.begin(), out.end()); + assert(std::equal(in, in + N, out.begin(), out.end())); + } + + // Conversions, (range) overload. + { + constexpr int N = 3; + double in[N] = {1.0, 2.0, 3.0}; + Buffer out; + + std::ranges::uninitialized_move(in, out); + assert(std::equal(in, in + N, out.begin(), out.end())); + } + + // Destination range is shorter than the source range, (iter, sentinel) overload. + { + constexpr int M = 3; + 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 == M); + assert(Counted::total_objects == M); + assert(Counted::total_moves == M); + assert(Counted::total_copies == 0); + + assert(std::equal(in, in + M, out.begin(), out.end())); + assert(result.in == in + M); + assert(result.out == out.end()); + } + + // Destination range is shorter than the source range, (range) overload. + { + constexpr int M = 3; + constexpr int N = 5; + Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; + Buffer out; + Counted::reset(); + + std::ranges::subrange range(in, in + N); + auto result = std::ranges::uninitialized_move(range, out); + assert(Counted::current_objects == M); + assert(Counted::total_objects == M); + assert(Counted::total_moves == M); + assert(Counted::total_copies == 0); + + assert(std::equal(in, in + M, out.begin(), out.end())); + assert(result.in == in + M); + assert(result.out == out.end()); + } + + // Ensure the `iter_move` customization point is being used. + { + 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; + + std::ranges::subrange range(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,204 @@ +//===----------------------------------------------------------------------===// +// +// 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" + +// TODO(varconst): consolidate the ADL checks into a single file. +// 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); + +static_assert(std::is_invocable_v); +struct NotConvertibleFromInt {}; +static_assert(!std::is_invocable_v); + +namespace adl { + +static int iter_move_invocations = 0; + +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 T&& iter_move(Iterator iter) { + ++iter_move_invocations; + return std::move(*iter); + } + + friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr == rhs.ptr; } +}; + +} // namespace adl + +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_SAME_TYPE(decltype(result), std::ranges::uninitialized_move_n_result); + + 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 out[3]. + try { + std::ranges::uninitialized_move_n(in, 5, out.begin(), out.end()); + assert(false); + } 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(in, N, out.cbegin(), out.cend()); + 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(); + + // Conversions. + { + constexpr int N = 3; + double in[N] = {1.0, 2.0, 3.0}; + Buffer out; + + std::ranges::uninitialized_move_n(in, N, out.begin(), out.end()); + assert(std::equal(in, in + N, out.begin(), out.end())); + } + + // Destination range is shorter than the source range. + { + constexpr int M = 3; + 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 == M); + assert(Counted::total_objects == M); + assert(Counted::total_moves == M); + assert(Counted::total_copies == 0); + + assert(std::equal(in, in + M, out.begin(), out.end())); + assert(result.in == in + M); + assert(result.out == out.end()); + } + + // Ensure the `iter_move` customization point is being used. + { + 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; + + std::ranges::subrange range(begin, end); + std::ranges::uninitialized_move(range, out); + assert(adl::iter_move_invocations == 3); + adl::iter_move_invocations = 0; + } + + return 0; +}