[libcxx-commits] [libcxx] 8d23b74 - [libc++][ranges] Implement `uninitialized_copy{, _n}` and `uninitialized_move{, _n}`.
Konstantin Varlamov via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Jan 10 22:50:28 PST 2022
Author: Konstantin Varlamov
Date: 2022-01-10T22:49:50-08:00
New Revision: 8d23b7420c92ddf8c3e5da39a90a1982fc72c231
URL: https://github.com/llvm/llvm-project/commit/8d23b7420c92ddf8c3e5da39a90a1982fc72c231
DIFF: https://github.com/llvm/llvm-project/commit/8d23b7420c92ddf8c3e5da39a90a1982fc72c231.diff
LOG: [libc++][ranges] Implement `uninitialized_copy{,_n}` and `uninitialized_move{,_n}`.
Also implement `in_out_result` which is a prerequisite.
Differential Revision: https://reviews.llvm.org/D116023
Added:
libcxx/include/__algorithm/in_out_result.h
libcxx/test/libcxx/diagnostics/detail.headers/algorithm/in_out_result.module.verify.cpp
libcxx/test/std/algorithms/algorithms.results/in_out_result.compile.pass.cpp
libcxx/test/std/algorithms/algorithms.results/in_out_result.pass.cpp
libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy.pass.cpp
libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy_n.pass.cpp
libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp
libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp
Modified:
libcxx/docs/Status/RangesAlgorithms.csv
libcxx/docs/Status/RangesPaper.csv
libcxx/include/CMakeLists.txt
libcxx/include/__memory/ranges_uninitialized_algorithms.h
libcxx/include/__memory/uninitialized_algorithms.h
libcxx/include/algorithm
libcxx/include/memory
libcxx/include/module.modulemap
libcxx/test/std/utilities/memory/specialized.algorithms/counted.h
libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct.pass.cpp
libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct_n.pass.cpp
libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct_n.pass.cpp
libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct.pass.cpp
libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct_n.pass.cpp
libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/ranges_uninitialized_fill_n.pass.cpp
libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/ranges_uninitialized_fill.pass.cpp
Removed:
################################################################################
diff --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv
index 0d8beb37984c1..2636fca2d838d 100644
--- a/libcxx/docs/Status/RangesAlgorithms.csv
+++ b/libcxx/docs/Status/RangesAlgorithms.csv
@@ -84,12 +84,12 @@ Permutation,pop_heap,Not assigned,n/a,Not started
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 <https://llvm.org/D116023>`_,✅
+Uninitialised memory,uninitialized_copy_n,Konstantin Varlamov,`D116023 <https://llvm.org/D116023>`_,✅
Uninitialised memory,uninitialized_fill,Konstantin Varlamov,`D115626 <https://llvm.org/D115626>`_,✅
Uninitialised memory,uninitialized_fill_n,Konstantin Varlamov,`D115626 <https://llvm.org/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 <https://llvm.org/D116023>`_,✅
+Uninitialised memory,uninitialized_move_n,Konstantin Varlamov,`D116023 <https://llvm.org/D116023>`_,✅
Uninitialised memory,uninitialized_default_construct,Konstantin Varlamov,`D115315 <https://llvm.org/D115315>`_,✅
Uninitialised memory,uninitialized_default_construct_n,Konstantin Varlamov,`D115315 <https://llvm.org/D115315>`_,✅
Uninitialised memory,uninitialized_value_construct,Konstantin Varlamov,`D115626 <https://llvm.org/D115626>`_,✅
diff --git a/libcxx/docs/Status/RangesPaper.csv b/libcxx/docs/Status/RangesPaper.csv
index 2c972f27e0047..ef6aacc0ac523 100644
--- a/libcxx/docs/Status/RangesPaper.csv
+++ b/libcxx/docs/Status/RangesPaper.csv
@@ -22,10 +22,10 @@ Section,Description,Dependencies,Assignee,Complete
| `ranges::uninitialized_default_construct_n <https://llvm.org/D115315>`_
| `ranges::uninitialized_value_construct <https://llvm.org/D115626>`_
| `ranges::uninitialized_value_construct_n <https://llvm.org/D115626>`_
-| ranges::uninitialized_copy
-| ranges::uninitialized_copy_n
-| ranges::uninitialized_move
-| ranges::uninitialized_move_n
+| `ranges::uninitialized_copy <https://llvm.org/D116023>`_
+| `ranges::uninitialized_copy_n <https://llvm.org/D116023>`_
+| `ranges::uninitialized_move <https://llvm.org/D116023>`_
+| `ranges::uninitialized_move_n <https://llvm.org/D116023>`_
| `ranges::uninitialized_fill <https://llvm.org/D115626>`_
| `ranges::uninitialized_fill_n <https://llvm.org/D115626>`_
| ranges::construct_at
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index de7b124e3f76a..7efe0383d80b6 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -26,6 +26,7 @@ set(files
__algorithm/generate.h
__algorithm/generate_n.h
__algorithm/half_positive.h
+ __algorithm/in_out_result.h
__algorithm/includes.h
__algorithm/inplace_merge.h
__algorithm/is_heap.h
diff --git a/libcxx/include/__algorithm/in_out_result.h b/libcxx/include/__algorithm/in_out_result.h
new file mode 100644
index 0000000000000..9d971157200fa
--- /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<class _InputIterator, class _OutputIterator>
+struct in_out_result {
+ [[no_unique_address]] _InputIterator in;
+ [[no_unique_address]] _OutputIterator out;
+
+ template <class _InputIterator2, class _OutputIterator2>
+ requires convertible_to<const _InputIterator&, _InputIterator2> && convertible_to<const _OutputIterator&,
+ _OutputIterator2>
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr operator in_out_result<_InputIterator2, _OutputIterator2>() const & {
+ return {in, out};
+ }
+
+ template <class _InputIterator2, class _OutputIterator2>
+ 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
index 6ec803806c05a..09786ffc69731 100644
--- 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>
@@ -61,8 +64,8 @@ struct __fn final : private __function_like {
} // namespace __uninitialized_default_construct
inline namespace __cpo {
-inline constexpr auto uninitialized_default_construct =
- __uninitialized_default_construct::__fn(__function_like::__tag());
+ inline constexpr auto uninitialized_default_construct =
+ __uninitialized_default_construct::__fn(__function_like::__tag());
} // namespace __cpo
// uninitialized_default_construct_n
@@ -87,8 +90,8 @@ struct __fn final : private __function_like {
} // namespace __uninitialized_default_construct_n
inline namespace __cpo {
-inline constexpr auto uninitialized_default_construct_n =
- __uninitialized_default_construct_n::__fn(__function_like::__tag());
+ inline constexpr auto uninitialized_default_construct_n =
+ __uninitialized_default_construct_n::__fn(__function_like::__tag());
} // namespace __cpo
// uninitialized_value_construct
@@ -119,8 +122,8 @@ struct __fn final : private __function_like {
} // namespace __uninitialized_value_construct
inline namespace __cpo {
-inline constexpr auto uninitialized_value_construct =
- __uninitialized_value_construct::__fn(__function_like::__tag());
+ inline constexpr auto uninitialized_value_construct =
+ __uninitialized_value_construct::__fn(__function_like::__tag());
} // namespace __cpo
// uninitialized_value_construct_n
@@ -144,8 +147,8 @@ struct __fn final : private __function_like {
} // namespace __uninitialized_value_construct_n
inline namespace __cpo {
-inline constexpr auto uninitialized_value_construct_n =
- __uninitialized_value_construct_n::__fn(__function_like::__tag());
+ inline constexpr auto uninitialized_value_construct_n =
+ __uninitialized_value_construct_n::__fn(__function_like::__tag());
} // namespace __cpo
// uninitialized_fill
@@ -173,10 +176,10 @@ struct __fn final : private __function_like {
};
-} // namespace __uninitialized_fil
+} // namespace __uninitialized_fill
inline namespace __cpo {
-inline constexpr auto uninitialized_fill = __uninitialized_fill::__fn(__function_like::__tag());
+ inline constexpr auto uninitialized_fill = __uninitialized_fill::__fn(__function_like::__tag());
} // namespace __cpo
// uninitialized_fill_n
@@ -201,7 +204,168 @@ struct __fn final : private __function_like {
} // namespace __uninitialized_fill_n
inline namespace __cpo {
-inline constexpr auto uninitialized_fill_n = __uninitialized_fill_n::__fn(__function_like::__tag());
+ inline constexpr auto uninitialized_fill_n = __uninitialized_fill_n::__fn(__function_like::__tag());
+} // namespace __cpo
+
+// uninitialized_copy
+
+template <class _InputIterator, class _OutputIterator>
+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 <input_iterator _InputIterator,
+ sentinel_for<_InputIterator> _Sentinel1,
+ __nothrow_forward_iterator _OutputIterator,
+ __nothrow_sentinel_for<_OutputIterator> _Sentinel2>
+ requires constructible_from<iter_value_t<_OutputIterator>, 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<iter_reference_t<_OutputIterator>>;
+
+ 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 <input_range _InputRange, __nothrow_forward_range _OutputRange>
+ requires constructible_from<range_value_t<_OutputRange>, range_reference_t<_InputRange>>
+ uninitialized_copy_result<borrowed_iterator_t<_InputRange>, 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 <class _InputIterator, class _OutputIterator>
+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 <input_iterator _InputIterator,
+ __nothrow_forward_iterator _OutputIterator,
+ __nothrow_sentinel_for<_OutputIterator> _Sentinel>
+ requires constructible_from<iter_value_t<_OutputIterator>, iter_reference_t<_InputIterator>>
+ uninitialized_copy_n_result<_InputIterator, _OutputIterator>
+ operator()(_InputIterator __ifirst, iter_
diff erence_t<_InputIterator> __n,
+ _OutputIterator __ofirst, _Sentinel __olast) const {
+ // clang-format on
+ using _ValueType = remove_reference_t<iter_reference_t<_OutputIterator>>;
+
+ 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 <class _InputIterator, class _OutputIterator>
+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 <input_iterator _InputIterator,
+ sentinel_for<_InputIterator> _Sentinel1,
+ __nothrow_forward_iterator _OutputIterator,
+ __nothrow_sentinel_for<_OutputIterator> _Sentinel2>
+ requires constructible_from<iter_value_t<_OutputIterator>, 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<iter_reference_t<_OutputIterator>>;
+ 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 <input_range _InputRange, __nothrow_forward_range _OutputRange>
+ requires constructible_from<range_value_t<_OutputRange>, range_reference_t<_InputRange>>
+ uninitialized_move_result<borrowed_iterator_t<_InputRange>, 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 <class _InputIterator, class _OutputIterator>
+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 <input_iterator _InputIterator,
+ __nothrow_forward_iterator _OutputIterator,
+ __nothrow_sentinel_for<_OutputIterator> _Sentinel>
+ requires constructible_from<iter_value_t<_OutputIterator>, iter_reference_t<_InputIterator>>
+ uninitialized_move_n_result<_InputIterator, _OutputIterator>
+ operator()(_InputIterator __ifirst, iter_
diff erence_t<_InputIterator> __n, _OutputIterator __ofirst,
+ _Sentinel __olast) const {
+ // clang-format on
+ using _ValueType = remove_reference_t<iter_reference_t<_OutputIterator>>;
+ 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
diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 69132633a48e3..40e7c79a51e0f 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -23,52 +23,75 @@
_LIBCPP_BEGIN_NAMESPACE_STD
-template <class _InputIterator, class _ForwardIterator>
-_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 <class _Iter>
+ _LIBCPP_HIDE_FROM_ABI friend _LIBCPP_CONSTEXPR bool operator!=(const _Iter&, __unreachable_sentinel) _NOEXCEPT {
+ return true;
+ }
+};
+
+// uninitialized_copy
+
+template <class _ValueType, class _InputIterator, class _Sentinel1, class _ForwardIterator, class _Sentinel2>
+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 pair<_InputIterator, _ForwardIterator>(_VSTD::move(__ifirst), _VSTD::move(__idx));
}
-template <class _InputIterator, class _Size, class _ForwardIterator>
-_ForwardIterator
-uninitialized_copy_n(_InputIterator __f, _Size __n, _ForwardIterator __r)
-{
- typedef typename iterator_traits<_ForwardIterator>::value_type value_type;
+template <class _InputIterator, class _ForwardIterator>
+_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 <class _ValueType, class _InputIterator, class _Size, class _ForwardIterator, class _Sentinel>
+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 pair<_InputIterator, _ForwardIterator>(_VSTD::move(__ifirst), _VSTD::move(__idx));
+}
+
+template <class _InputIterator, class _Size, class _ForwardIterator>
+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 +276,71 @@ _ForwardIterator uninitialized_value_construct_n(_ForwardIterator __first, _Size
return __uninitialized_value_construct_n<_ValueType>(_VSTD::move(__first), __n);
}
-template <class _InputIt, class _ForwardIt>
-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 <class _ValueType, class _InputIterator, class _Sentinel1, class _ForwardIterator, class _Sentinel2,
+ class _IterMove>
+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 <class _InputIterator, class _ForwardIterator>
+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 <class _InputIt, class _Size, class _ForwardIt>
-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 <class _ValueType, class _InputIterator, class _Size, class _ForwardIterator, class _Sentinel, class _IterMove>
+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 <class _InputIterator, class _Size, class _ForwardIterator>
+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
index 55b0bc5249c34..932d17d66b176 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -641,6 +641,12 @@ template <class BidirectionalIterator, class Compare>
constexpr bool // constexpr in C++20
prev_permutation(BidirectionalIterator first, BidirectionalIterator last, Compare comp);
+namespace ranges {
+// [algorithms.results], algorithm result types
+template<class InputIterator, class OutputIterator>
+ struct in_out_result;
+}
+
} // std
*/
@@ -685,6 +691,7 @@ template <class BidirectionalIterator, class Compare>
#include <__algorithm/generate.h>
#include <__algorithm/generate_n.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
index 3ed1530f2a756..244b40fe7720d 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -181,28 +181,65 @@ template <class InputIterator, class ForwardIterator>
ForwardIterator
uninitialized_copy(InputIterator first, InputIterator last, ForwardIterator result);
+namespace ranges {
+
+template<class InputIterator, class OutputIterator>
+using uninitialized_copy_result = in_out_result<InputIterator, OutputIterator>; // since C++20
+
+template<input_iterator InputIterator, sentinel-for<InputIterator> Sentinel1, nothrow-forward-iterator OutputIterator, nothrow-sentinel-for<OutputIterator> Sentinel2>
+ requires constructible_from<iter_value_t<OutputIterator>, iter_reference_t<InputIterator>>
+uninitialized_copy_result<InputIterator, OutputIterator>
+uninitialized_copy(InputIterator ifirst, Sentinel1 ilast, OutputIterator ofirst, Sentinel2 olast); // since C++20
+
+template<input_range InputRange, nothrow-forward-range OutputRange>
+ requires constructible_from<range_value_t<OutputRange>, range_reference_t<InputRange>>
+uninitialized_copy_result<borrowed_iterator_t<InputRange>, borrowed_iterator_t<OutputRange>>
+uninitialized_copy(InputRange&& in_range, OutputRange&& out_range); // since C++20
+
+}
+
template <class InputIterator, class Size, class ForwardIterator>
ForwardIterator
uninitialized_copy_n(InputIterator first, Size n, ForwardIterator result);
+namespace ranges {
+
+template<class InputIterator, class OutputIterator>
+using uninitialized_copy_n_result = in_out_result<InputIterator, OutputIterator>; // since C++20
+
+template<input_iterator InputIterator, nothrow-forward-iterator OutputIterator, nothrow-sentinel-for<OutputIterator> Sentinel>
+ requires constructible_from<iter_value_t<OutputIterator>, iter_reference_t<InputIterator>>
+uninitialized_copy_n_result<InputIterator, OutputIterator>
+uninitialized_copy_n(InputIterator ifirst, iter_
diff erence_t<InputIterator> n, OutputIterator ofirst, Sentinel olast); // since C++20
+
+}
+
template <class ForwardIterator, class T>
void uninitialized_fill(ForwardIterator first, ForwardIterator last, const T& x);
+namespace ranges {
+
template <nothrow-forward-iterator ForwardIterator, nothrow-sentinel-for<ForwardIterator> Sentinel, class T>
requires constructible_from<iter_value_t<ForwardIterator>, 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 <nothrow-forward-range ForwardRange, class T>
requires constructible_from<range_value_t<ForwardRange>, const T&>
-borrowed_iterator_t<ForwardRange> ranges::uninitialized_fill(ForwardRange&& range, const T& x); // since C++20
+borrowed_iterator_t<ForwardRange> uninitialized_fill(ForwardRange&& range, const T& x); // since C++20
+
+}
template <class ForwardIterator, class Size, class T>
ForwardIterator
uninitialized_fill_n(ForwardIterator first, Size n, const T& x);
+namespace ranges {
+
template <nothrow-forward-iterator ForwardIterator, class T>
requires constructible_from<iter_value_t<ForwardIterator>, const T&>
-ForwardIterator ranges::uninitialized_fill_n(ForwardIterator first, iter_
diff erence_t<ForwardIterator> n); // since C++20
+ForwardIterator uninitialized_fill_n(ForwardIterator first, iter_
diff erence_t<ForwardIterator> n); // since C++20
+
+}
template <class T, class ...Args>
constexpr T* construct_at(T* location, Args&& ...args); // since C++20
@@ -219,44 +256,89 @@ ForwardIterator destroy_n(ForwardIterator first, Size n); // constexpr in C++20
template <class InputIterator, class ForwardIterator>
ForwardIterator uninitialized_move(InputIterator first, InputIterator last, ForwardIterator result);
+namespace ranges {
+
+template<class InputIterator, class OutputIterator>
+using uninitialized_move_result = in_out_result<InputIterator, OutputIterator>; // since C++20
+
+template <input_iterator InputIterator, sentinel_for<InputIterator> Sentinel1, nothrow-forward-iterator OutputIterator, nothrow-sentinel-for<O> Sentinel2>
+ requires constructible_from<iter_value_t<OutputIterator>, iter_rvalue_reference_t<InputIterator>>
+uninitialized_move_result<InputIterator, OutputIterator>
+uninitialized_move(InputIterator ifirst, Sentinel1 ilast, OutputIterator ofirst, Sentinel2 olast); // since C++20
+
+template<input_range InputRange, nothrow-forward-range OutputRange>
+ requires constructible_from<range_value_t<OutputRange>, range_rvalue_reference_t<InputRange>>
+uninitialized_move_result<borrowed_iterator_t<InputRange>, borrowed_iterator_t<OutputRange>>
+uninitialized_move(InputRange&& in_range, OutputRange&& out_range); // since C++20
+
+}
+
template <class InputIterator, class Size, class ForwardIterator>
pair<InputIterator,ForwardIterator> uninitialized_move_n(InputIterator first, Size n, ForwardIterator result);
+namespace ranges {
+
+template<class InputIterator, class OutputIterator>
+using uninitialized_move_n_result = in_out_result<InputIterator, OutputIterator>; // since C++20
+
+template<input_iterator InputIterator, nothrow-forward-iterator OutputIterator, nothrow-sentinel-for<OutputIterator> Sentinel>
+ requires constructible_from<iter_value_t<OutputIterator>, iter_rvalue_reference_t<InputIterator>>
+uninitialized_move_n_result<InputIterator, OutputIterator>
+uninitialized_move_n(InputIterator ifirst, iter_
diff erence_t<InputIterator> n, OutputIterator ofirst, Sentinel olast); // since C++20
+
+}
+
template <class ForwardIterator>
void uninitialized_value_construct(ForwardIterator first, ForwardIterator last);
+namespace ranges {
+
template <nothrow-forward-iterator ForwardIterator, nothrow-sentinel-for<ForwardIterator> Sentinel>
requires default_initializable<iter_value_t<ForwardIterator>>
- ForwardIterator ranges::uninitialized_value_construct(ForwardIterator first, Sentinel last); // since C++20
+ ForwardIterator uninitialized_value_construct(ForwardIterator first, Sentinel last); // since C++20
template <nothrow-forward-range ForwardRange>
requires default_initializable<range_value_t<ForwardRange>>
- borrowed_iterator_t<ForwardRange> ranges::uninitialized_value_construct(ForwardRange&& r); // since C++20
+ borrowed_iterator_t<ForwardRange> uninitialized_value_construct(ForwardRange&& r); // since C++20
+
+}
template <class ForwardIterator, class Size>
ForwardIterator uninitialized_value_construct_n(ForwardIterator first, Size n);
+namespace ranges {
+
template <nothrow-forward-iterator ForwardIterator>
requires default_initializable<iter_value_t<ForwardIterator>>
- ForwardIterator ranges::uninitialized_value_construct_n(ForwardIterator first, iter_
diff erence_t<ForwardIterator> n); // since C++20
+ ForwardIterator uninitialized_value_construct_n(ForwardIterator first, iter_
diff erence_t<ForwardIterator> n); // since C++20
+
+}
template <class ForwardIterator>
void uninitialized_default_construct(ForwardIterator first, ForwardIterator last);
+namespace ranges {
+
template <nothrow-forward-iterator ForwardIterator, nothrow-sentinel-for<ForwardIterator> Sentinel>
requires default_initializable<iter_value_t<ForwardIterator>>
- ForwardIterator ranges::uninitialized_default_construct(ForwardIterator first, Sentinel last); // since C++20
+ ForwardIterator uninitialized_default_construct(ForwardIterator first, Sentinel last); // since C++20
template <nothrow-forward-range ForwardRange>
requires default_initializable<range_value_t<ForwardRange>>
- borrowed_iterator_t<ForwardRange> ranges::uninitialized_default_construct(ForwardRange&& r); // since C++20
+ borrowed_iterator_t<ForwardRange> uninitialized_default_construct(ForwardRange&& r); // since C++20
+
+}
template <class ForwardIterator, class Size>
ForwardIterator uninitialized_default_construct_n(ForwardIterator first, Size n);
+namespace ranges {
+
template <nothrow-forward-iterator ForwardIterator>
requires default_initializable<iter_value_t<ForwardIterator>>
- ForwardIterator ranges::uninitialized_default_construct_n(ForwardIterator first, iter_
diff erence_t<ForwardIterator> n); // since C++20
+ ForwardIterator uninitialized_default_construct_n(ForwardIterator first, iter_
diff erence_t<ForwardIterator> n); // since C++20
+
+}
template <class Y> struct auto_ptr_ref {}; // deprecated in C++11, removed in C++17
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 3ee19ab1b8f88..305fe1bc9d35f 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -246,6 +246,7 @@ module std [system] {
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
index 0000000000000..254aca51128f4
--- /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.compile.pass.cpp b/libcxx/test/std/algorithms/algorithms.results/in_out_result.compile.pass.cpp
new file mode 100644
index 0000000000000..081f52c811dd0
--- /dev/null
+++ b/libcxx/test/std/algorithms/algorithms.results/in_out_result.compile.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+// clang-cl and cl currently don't support [[no_unique_address]]
+// XFAIL: msvc
+
+// namespace ranges {
+// template<class InputIterator, class OutputIterator>
+// struct in_out_result;
+// }
+
+#include <algorithm>
+
+// Size optimization.
+struct Empty {};
+struct Empty2 {};
+
+static_assert(sizeof(std::ranges::in_out_result<Empty, int>) == sizeof(int));
+static_assert(sizeof(std::ranges::in_out_result<int, Empty>) == sizeof(int));
+static_assert(sizeof(std::ranges::in_out_result<Empty, Empty2>) == sizeof(char));
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
index 0000000000000..2fb02fba6f10c
--- /dev/null
+++ b/libcxx/test/std/algorithms/algorithms.results/in_out_result.pass.cpp
@@ -0,0 +1,132 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <algorithm>
+//
+// namespace ranges {
+// template<class InputIterator, class OutputIterator>
+// struct in_out_result;
+// }
+
+#include <algorithm>
+#include <cassert>
+#include <type_traits>
+
+struct A {
+ A(int&);
+};
+static_assert(!std::is_constructible_v<std::ranges::in_out_result<A, A>, std::ranges::in_out_result<int, int>&>);
+
+static_assert(std::is_convertible_v<std::ranges::in_out_result<int, int>&,
+ std::ranges::in_out_result<long, long>>);
+static_assert(!std::is_nothrow_convertible_v<std::ranges::in_out_result<int, int>&,
+ std::ranges::in_out_result<long, long>>);
+static_assert(std::is_convertible_v<const std::ranges::in_out_result<int, int>&,
+ std::ranges::in_out_result<long, long>>);
+static_assert(!std::is_nothrow_convertible_v<const std::ranges::in_out_result<int, int>&,
+ std::ranges::in_out_result<long, long>>);
+static_assert(std::is_convertible_v<std::ranges::in_out_result<int, int>&&,
+ std::ranges::in_out_result<long, long>>);
+static_assert(!std::is_nothrow_convertible_v<std::ranges::in_out_result<int, int>&&,
+ std::ranges::in_out_result<long, long>>);
+static_assert(std::is_convertible_v<const std::ranges::in_out_result<int, int>&&,
+ std::ranges::in_out_result<long, long>>);
+static_assert(!std::is_nothrow_convertible_v<const std::ranges::in_out_result<int, int>&&,
+ std::ranges::in_out_result<long, long>>);
+
+int main(int, char**) {
+ // Conversion, fundamental types.
+ {
+ std::ranges::in_out_result<int, bool> x = {2, false};
+ std::ranges::in_out_result<double, char> 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<From1, From2> x{42, 99};
+ std::ranges::in_out_result<To1, To2> y = x;
+ assert(y.in.value == 42);
+ assert(y.out.value == 99);
+ }
+
+ // Copy-only type.
+ {
+ struct CopyOnly {
+ int value = 0;
+ CopyOnly() = default;
+ CopyOnly(int v) : value(v) {}
+
+ CopyOnly(const CopyOnly&) = default;
+ CopyOnly(CopyOnly&&) = delete;
+ };
+
+ std::ranges::in_out_result<CopyOnly, CopyOnly> x;
+ x.in.value = 42;
+ x.out.value = 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<MoveOnly, MoveOnly> 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<Foo1, Foo2>, std::ranges::in_out_result<Bar1, Bar2>>);
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/counted.h b/libcxx/test/std/utilities/memory/specialized.algorithms/counted.h
index 384fa7d78f4b9..1d4cc5229a59b 100644
--- 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 @@ struct Counted {
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 @@ struct Counted {
};
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
index 04ee5b8e4d6eb..59c149c21e11a 100644
--- 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
diff erent mechanism to achieve this effect, so this check is
// libc++-specific.
@@ -154,8 +155,7 @@ int main(int, char**) {
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
index 904366fe315c4..9836a3e74f19e 100644
--- 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
diff erent 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
index 95fb90d6f2a8f..58ee06aaa819d 100644
--- 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 @@ void test_ctor_throws()
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
index cc89c69514b93..7c46558f2d471 100644
--- 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
diff erent mechanism to achieve this effect, so this check is
// libc++-specific.
@@ -173,8 +174,7 @@ int main(int, char**) {
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
index 3207f286d14b7..ea8ae75cbd704 100644
--- 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
diff erent 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
index 0000000000000..f6b40fd145635
--- /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
+
+// <memory>
+//
+// template<input_iterator I, sentinel-for<I> S1, nothrow-forward-iterator O, nothrow-sentinel-for<O> S2>
+// requires constructible_from<iter_value_t<O>, iter_reference_t<I>>
+// uninitialized_copy_result<I, O> ranges::uninitialized_copy(I ifirst, S1 ilast, O ofirst, S2 olast); // since C++20
+//
+// template<input_range IR, nothrow-forward-range OR>
+// requires constructible_from<range_value_t<OR>, range_reference_t<IR>>
+// uninitialized_copy_result<borrowed_iterator_t<IR>, borrowed_iterator_t<OR>> ranges::uninitialized_copy(IR&& in_range, OR&& out_range); // since C++20
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <memory>
+#include <ranges>
+#include <type_traits>
+
+#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
diff erent mechanism to achieve this effect, so this check is
+// libc++-specific.
+LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::uninitialized_copy)>);
+
+static_assert(std::is_invocable_v<decltype(std::ranges::uninitialized_copy), int*, int*, long*, long*>);
+struct NotConvertibleFromInt {};
+static_assert(!std::is_invocable_v<decltype(std::ranges::uninitialized_copy), int*, int*, NotConvertibleFromInt*,
+ NotConvertibleFromInt*>);
+
+int main(int, char**) {
+ // An empty range -- no default constructors should be invoked.
+ {
+ Counted in[] = {Counted()};
+ Buffer<Counted, 1> 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<Counted> 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<Counted*> it(in);
+ std::ranges::subrange range(it, sentinel_wrapper<forward_iterator<Counted*>>(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<Counted*> it(in);
+ std::ranges::subrange range(it, sentinel_wrapper<forward_iterator<Counted*>>(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<Counted, N> 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<Counted*, Counted*>);
+
+ 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<Counted, N> 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<Counted*, Counted*>);
+
+ 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<Counted, 5> 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<Counted, 5> 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<Counted, 5> 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<Counted, N> 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<Counted, N> 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<int, N> 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<int, N> 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<Counted, M> 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<Counted, M> 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
index 0000000000000..f32af6cf4b38b
--- /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
+
+// <memory>
+//
+// template<input_iterator I, nothrow-forward-iterator O, nothrow-sentinel-for<O> S>
+// requires constructible_from<iter_value_t<O>, iter_reference_t<I>>
+// uninitialized_copy_n_result<I, O> uninitialized_copy_n(I ifirst, iter_
diff erence_t<I> n, O ofirst, S olast); // since C++20
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <memory>
+#include <ranges>
+#include <type_traits>
+
+#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
diff erent mechanism to achieve this effect, so this check is
+// libc++-specific.
+LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::uninitialized_copy_n)>);
+
+static_assert(std::is_invocable_v<decltype(std::ranges::uninitialized_copy_n), int*, size_t, long*, long*>);
+struct NotConvertibleFromInt {};
+static_assert(!std::is_invocable_v<decltype(std::ranges::uninitialized_copy_n), int*, size_t, NotConvertibleFromInt*,
+ NotConvertibleFromInt*>);
+
+int main(int, char**) {
+ // An empty range -- no default constructors should be invoked.
+ {
+ Counted in[] = {Counted()};
+ Buffer<Counted, 1> 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<Counted, N> 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<Counted*, Counted*>);
+
+ 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<Counted, N> 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<int, N> 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<Counted, M> 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
index a93ab532f2cea..9807f1d3ab498 100644
--- 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
diff erent mechanism to achieve this effect, so this check is
// libc++-specific.
LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::uninitialized_fill_n)>);
struct NotConvertibleFromInt {};
-static_assert(!std::is_invocable_v<decltype(std::ranges::uninitialized_fill_n), NotConvertibleFromInt*,
- NotConvertibleFromInt*, int>);
+static_assert(!std::is_invocable_v<decltype(std::ranges::uninitialized_fill_n), NotConvertibleFromInt*, size_t, int>);
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
index d9135a036eebb..e6318297ad759 100644
--- 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
diff erent mechanism to achieve this effect, so this check is
// libc++-specific.
@@ -187,8 +188,7 @@ int main(int, char**) {
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
index 0000000000000..934ac6a4f23fe
--- /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
+
+// <memory>
+//
+// template<input_iterator InputIterator, nothrow-forward-iterator OutputIterator, nothrow-sentinel-for<OutputIterator> Sentinel>
+// requires constructible_from<iter_value_t<OutputIterator>, iter_rvalue_reference_t<InputIterator>>
+// ranges::uninitialized_move_n_result<InputIterator, OutputIterator>
+// ranges::uninitialized_move_n(InputIterator ifirst, iter_
diff erence_t<InputIterator> n, OutputIterator ofirst, Sentinel olast); // since C++20
+
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <memory>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#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
diff erent mechanism to achieve this effect, so this check is
+// libc++-specific.
+LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::uninitialized_move)>);
+
+static_assert(std::is_invocable_v<decltype(std::ranges::uninitialized_move), int*, int*, long*, long*>);
+struct NotConvertibleFromInt {};
+static_assert(!std::is_invocable_v<decltype(std::ranges::uninitialized_move), int*, int*, NotConvertibleFromInt*,
+ NotConvertibleFromInt*>);
+
+namespace adl {
+
+static int iter_move_invocations = 0;
+
+template <class T>
+struct Iterator {
+ using value_type = T;
+ using
diff erence_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<Counted, 1> 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<Counted> 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<Counted*> it(in);
+ std::ranges::subrange range(it, sentinel_wrapper<forward_iterator<Counted*>>(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<Counted*> it(in);
+ std::ranges::subrange range(it, sentinel_wrapper<forward_iterator<Counted*>>(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<Counted, N> 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<Counted*, Counted*>);
+ 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<Counted, N> 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<Counted*, Counted*>);
+
+ 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<Counted, 5> 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<Counted, 5> 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<Counted, 5> 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<Counted, 5> 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<Counted, 5> 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<Counted, N> 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<Counted, N> 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<int, N> 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<int, N> 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<Counted, M> 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<Counted, M> 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<int, N> out;
+ adl::Iterator<int> begin(in);
+ adl::Iterator<int> 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
index 0000000000000..9c6691de92297
--- /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
+
+// <memory>
+//
+// template<input_iterator I, nothrow-forward-iterator O, nothrow-sentinel-for<O> S>
+// requires constructible_from<iter_value_t<O>, iter_reference_t<I>>
+// uninitialized_copy_n_result<I, O> uninitialized_copy_n(I ifirst, iter_
diff erence_t<I> n, O ofirst, S olast); // since C++20
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <memory>
+#include <ranges>
+#include <type_traits>
+
+#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
diff erent mechanism to achieve this effect, so this check is
+// libc++-specific.
+LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::uninitialized_move_n)>);
+
+static_assert(std::is_invocable_v<decltype(std::ranges::uninitialized_move_n), int*, size_t, long*, long*>);
+struct NotConvertibleFromInt {};
+static_assert(!std::is_invocable_v<decltype(std::ranges::uninitialized_move_n), int*, size_t, NotConvertibleFromInt*,
+ NotConvertibleFromInt*>);
+
+namespace adl {
+
+static int iter_move_invocations = 0;
+
+template <class T>
+struct Iterator {
+ using value_type = T;
+ using
diff erence_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<Counted, 1> 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<Counted, N> 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<Counted*, Counted*>);
+
+ 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<Counted, 5> 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<Counted, N> 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<int, N> 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<Counted, M> 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<int, N> out;
+ adl::Iterator<int> begin(in);
+ adl::Iterator<int> 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;
+}
More information about the libcxx-commits
mailing list