[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
This commit is contained in:
Konstantin Varlamov 2022-01-10 22:49:37 -08:00
parent b28e8abfd0
commit 8d23b7420c
24 changed files with 1818 additions and 102 deletions

View File

@ -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>`_,✅

1 Category,Algorithm,Assignee,CL,Complete
84 Permutation,sort_heap,Not assigned,n/a,Not started
85 Permutation,prev_permutation,Not assigned,n/a,Not started
86 Permutation,next_permutation,Not assigned,n/a,Not started
87 Uninitialised memory,uninitialized_copy,Konstantin Varlamov,n/a,Not started Uninitialised memory,uninitialized_copy,Konstantin Varlamov,`D116023 <https://llvm.org/D116023>`_,✅
88 Uninitialised memory,uninitialized_copy_n,Konstantin Varlamov,n/a,Not started Uninitialised memory,uninitialized_copy_n,Konstantin Varlamov,`D116023 <https://llvm.org/D116023>`_,✅
89 Uninitialised memory,uninitialized_fill,Konstantin Varlamov,`D115626 <https://llvm.org/D115626>`_,✅
90 Uninitialised memory,uninitialized_fill_n,Konstantin Varlamov,`D115626 <https://llvm.org/D115626>`_,✅
91 Uninitialised memory,uninitialized_move,Konstantin Varlamov,n/a,Not started Uninitialised memory,uninitialized_move,Konstantin Varlamov,`D116023 <https://llvm.org/D116023>`_,✅
92 Uninitialised memory,uninitialized_move_n,Konstantin Varlamov,n/a,Not started Uninitialised memory,uninitialized_move_n,Konstantin Varlamov,`D116023 <https://llvm.org/D116023>`_,✅
93 Uninitialised memory,uninitialized_default_construct,Konstantin Varlamov,`D115315 <https://llvm.org/D115315>`_,✅
94 Uninitialised memory,uninitialized_default_construct_n,Konstantin Varlamov,`D115315 <https://llvm.org/D115315>`_,✅
95 Uninitialised memory,uninitialized_value_construct,Konstantin Varlamov,`D115626 <https://llvm.org/D115626>`_,✅

View File

@ -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

1 Section Description Dependencies Assignee Complete
22 [predef.iterators] Updates to predefined iterators. | [iterator.concepts] | [iterator.cust.swap] | [iterator.cust.move] Unassigned Not started
23 [move.sentinel] [predef.iterators] Unassigned Not started
24 [common.iterator] | [iterator.concepts] | [iterator.cust.swap] | [iterator.cust.move] Zoe Carver
25 [default.sentinels] std::default_sentinel_t. No dependencies Zoe Carver
26 [counted.iterator] | [iterator.concepts] | [iterator.cust.swap] | [iterator.cust.move] | [default.sentinels] Zoe Carver
27 [stream.iterators] [default.sentinels] Unassigned Not started
28 `[range.access] <http://wg21.link/range.access>`_ | `ranges::begin <https://llvm.org/D100255>`_ | `ranges::end <https://llvm.org/D100255>`_ | `range::cbegin <https://llvm.org/D100255>`_ | `ranges::cend <https://llvm.org/D100255>`_ | ranges::rbegin | ranges::rend | ranges::crbegin | ranges::crend | `ranges::size <https://llvm.org/D101079>`_ | `ranges::ssize <https://llvm.org/D101189>`_ | `ranges::empty <https://llvm.org/D101193>`_ | `ranges::data <https://llvm.org/D101476>`_ | ranges::cdata [iterator.concepts] Christopher Di Bella and Zoe Carver In progress
29 `[range.range] <http://wg21.link/range.range>`_ | `ranges::range <https://llvm.org/D100269>`_ | `ranges::borrowed_range <https://llvm.org/D102426>`_ | `ranges::enable_borrowed_range <https://llvm.org/D90999>`_ | `ranges::iterator_t <https://llvm.org/D100255>`_ | `ranges::sentinel_t <https://llvm.org/D100269>`_ | `ranges::range_difference_t <https://llvm.org/D100269>`_ | `ranges::range_size_t <https://llvm.org/D106708>`_ | `ranges::range_value_t <https://llvm.org/D100269>`_ | `ranges::range_reference_t <https://llvm.org/D100269>`_ | `ranges::range_rvalue_reference_t <https://llvm.org/D100269>`_ [range.access] Christopher Di Bella
30 `[range.sized] <http://wg21.link/range.sized>`_ | `ranges::sized_range <https://llvm.org/D102434>`_ | `ranges::disable_sized_range <https://llvm.org/D102434>`_ | [range.primitives] | [range.range] Christopher Di Bella
31 `[range.view] <http://wg21.link/range.view>`_ | `ranges::enable_view <https://llvm.org/D101547>`_ | `ranges::view_base <https://llvm.org/D101547>`_ | `ranges::view <https://llvm.org/D101547>`_ [range.range] Louis Dionne

View File

@ -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

View File

@ -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

View File

@ -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_difference_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_difference_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

View File

@ -23,52 +23,75 @@
_LIBCPP_BEGIN_NAMESPACE_STD
// 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
try {
#endif
for (; __ifirst != __ilast && __idx != __olast; ++__ifirst, (void)++__idx)
::new (_VSTD::__voidify(*__idx)) _ValueType(*__ifirst);
#ifndef _LIBCPP_NO_EXCEPTIONS
} catch (...) {
_VSTD::__destroy(__ofirst, __idx);
throw;
}
#endif
return pair<_InputIterator, _ForwardIterator>(_VSTD::move(__ifirst), _VSTD::move(__idx));
}
template <class _InputIterator, class _ForwardIterator>
_ForwardIterator
uninitialized_copy(_InputIterator __f, _InputIterator __l, _ForwardIterator __r)
{
typedef typename iterator_traits<_ForwardIterator>::value_type value_type;
_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 (; __f != __l; ++__f, (void) ++__r)
::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>
_ForwardIterator
uninitialized_copy_n(_InputIterator __f, _Size __n, _ForwardIterator __r)
{
typedef typename iterator_traits<_ForwardIterator>::value_type value_type;
#ifndef _LIBCPP_NO_EXCEPTIONS
_ForwardIterator __s = __r;
try
{
#endif
for (; __n > 0; ++__f, (void) ++__r, (void) --__n)
::new ((void*)_VSTD::addressof(*__r)) value_type(*__f);
#ifndef _LIBCPP_NO_EXCEPTIONS
}
catch (...)
{
for (; __s != __r; ++__s)
__s->~value_type();
throw;
}
#endif
return __r;
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 _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;
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);
}
// 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

View File

@ -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>

View File

@ -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_difference_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_difference_t<ForwardIterator> n); // since C++20
ForwardIterator uninitialized_fill_n(ForwardIterator first, iter_difference_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_difference_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_difference_t<ForwardIterator> n); // since C++20
ForwardIterator uninitialized_value_construct_n(ForwardIterator first, iter_difference_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_difference_t<ForwardIterator> n); // since C++20
ForwardIterator uninitialized_default_construct_n(ForwardIterator first, iter_difference_t<ForwardIterator> n); // since C++20
}
template <class Y> struct auto_ptr_ref {}; // deprecated in C++11, removed in C++17

View File

@ -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" }

View File

@ -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>

View File

@ -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));

View File

@ -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;
}

View File

@ -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 @@ private:
};
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

View File

@ -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 @@ 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);

View File

@ -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.

View File

@ -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
}

View File

@ -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 @@ 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);

View File

@ -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.

View File

@ -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 different 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;
}

View File

@ -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_difference_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 different 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;
}

View File

@ -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<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;

View File

@ -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 @@ 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);

View File

@ -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_difference_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 different 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 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<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;
}

View File

@ -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_difference_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 different 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 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<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;
}