forked from OSchip/llvm-project
[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:
parent
b28e8abfd0
commit
8d23b7420c
|
@ -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>`_,✅
|
||||
|
|
|
|
@ -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
|
||||
|
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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>
|
|
@ -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));
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue