[libc++][ranges] implement `std::ranges::unique{_copy}`

implement `std::ranges::unique` and `std::ranges::unique_copy`

Differential Revision: https://reviews.llvm.org/D130404
This commit is contained in:
Hui Xie 2022-07-23 01:44:25 +01:00
parent bf3714884a
commit 72f57e3a30
19 changed files with 891 additions and 176 deletions

View File

@ -57,7 +57,7 @@ Write,swap_ranges,Nikolas Klauser,`D116303 <https://llvm.org/D116303>`_,✅
Write,reverse_copy,Nikolas Klauser,`D127211 <https://llvm.org/D127211>`_,✅
Write,rotate_copy,Nikolas Klauser,`D127211 <https://llvm.org/D127211>`_,✅
Write,sample,Not assigned,n/a,Not started
Write,unique_copy,Not assigned,n/a,Not started
Write,unique_copy,Hui Xie,`D130404 <https://llvm.org/D130404>`,✅
Write,partition_copy,Konstantin Varlamov,`D130070 <https://llvm.org/D130070>`_,✅
Write,partial_sort_copy,Konstantin Varlamov,n/a,In progress
Merge,merge,Hui Xie,`D128611 <https://llvm.org/D128611>`_,✅
@ -70,7 +70,7 @@ Permutation,remove_if,Nikolas Klauser,`D128618 <https://llvm.org/D128618>`_,✅
Permutation,reverse,Nikolas Klauser,`D125752 <https://llvm.org/D125752>`_,✅
Permutation,rotate,Nikolas Klauser,`D124122 <https://llvm.org/D124122>`_,Under review
Permutation,shuffle,Konstantin Varlamov,`D130321 <https://llvm.org/D130321>`_,✅
Permutation,unique,Not assigned,n/a,Not started
Permutation,unique,Hui Xie,`D130404 <https://llvm.org/D130404>`,✅
Permutation,partition,Konstantin Varlamov,`D129624 <https://llvm.org/D129624>`_,✅
Permutation,stable_partition,Konstantin Varlamov,`D129624 <https://llvm.org/D129624>`_,✅
Permutation,sort,Konstantin Varlamov,`D127557 <https://llvm.org/D127557>`_,✅

1 Category Algorithm Assignee CL Complete
57 Write reverse_copy Nikolas Klauser `D127211 <https://llvm.org/D127211>`_
58 Write rotate_copy Nikolas Klauser `D127211 <https://llvm.org/D127211>`_
59 Write sample Not assigned n/a Not started
60 Write unique_copy Not assigned Hui Xie n/a `D130404 <https://llvm.org/D130404>` Not started
61 Write partition_copy Konstantin Varlamov `D130070 <https://llvm.org/D130070>`_
62 Write partial_sort_copy Konstantin Varlamov n/a In progress
63 Merge merge Hui Xie `D128611 <https://llvm.org/D128611>`_
70 Permutation reverse Nikolas Klauser `D125752 <https://llvm.org/D125752>`_
71 Permutation rotate Nikolas Klauser `D124122 <https://llvm.org/D124122>`_ Under review
72 Permutation shuffle Konstantin Varlamov `D130321 <https://llvm.org/D130321>`_
73 Permutation unique Not assigned Hui Xie n/a `D130404 <https://llvm.org/D130404>` Not started
74 Permutation partition Konstantin Varlamov `D129624 <https://llvm.org/D129624>`_
75 Permutation stable_partition Konstantin Varlamov `D129624 <https://llvm.org/D129624>`_
76 Permutation sort Konstantin Varlamov `D127557 <https://llvm.org/D127557>`_

View File

@ -11,8 +11,10 @@
#define _LIBCPP___ALGORITHM_ADJACENT_FIND_H
#include <__algorithm/comp.h>
#include <__algorithm/iterator_operations.h>
#include <__config>
#include <__iterator/iterator_traits.h>
#include <__utility/move.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@ -20,25 +22,31 @@
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _ForwardIterator, class _BinaryPredicate>
_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator
adjacent_find(_ForwardIterator __first, _ForwardIterator __last, _BinaryPredicate __pred) {
if (__first != __last) {
_ForwardIterator __i = __first;
while (++__i != __last) {
if (__pred(*__first, *__i))
return __first;
__first = __i;
}
template <class _Iter, class _Sent, class _BinaryPredicate>
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 _Iter
__adjacent_find(_Iter __first, _Sent __last, _BinaryPredicate&& __pred) {
if (__first == __last)
return __first;
_Iter __i = __first;
while (++__i != __last) {
if (__pred(*__first, *__i))
return __first;
__first = __i;
}
return __last;
return __i;
}
template <class _ForwardIterator, class _BinaryPredicate>
_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator
adjacent_find(_ForwardIterator __first, _ForwardIterator __last, _BinaryPredicate __pred) {
return std::__adjacent_find(std::move(__first), std::move(__last), __pred);
}
template <class _ForwardIterator>
_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator
_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator
adjacent_find(_ForwardIterator __first, _ForwardIterator __last) {
typedef typename iterator_traits<_ForwardIterator>::value_type __v;
return _VSTD::adjacent_find(__first, __last, __equal_to<__v>());
return std::adjacent_find(std::move(__first), std::move(__last), __equal_to<__v>());
}
_LIBCPP_END_NAMESPACE_STD

View File

@ -17,6 +17,7 @@
#include <__iterator/iter_swap.h>
#include <__iterator/iterator_traits.h>
#include <__iterator/next.h>
#include <__iterator/readable_traits.h>
#include <__utility/declval.h>
#include <__utility/forward.h>
#include <__utility/move.h>
@ -35,6 +36,10 @@ struct _RangeAlgPolicy {};
template <>
struct _IterOps<_RangeAlgPolicy> {
template <class _Iter>
using __value_type = iter_value_t<_Iter>;
static constexpr auto advance = ranges::advance;
static constexpr auto distance = ranges::distance;
static constexpr auto __iter_move = ranges::iter_move;
@ -50,6 +55,9 @@ struct _ClassicAlgPolicy {};
template <>
struct _IterOps<_ClassicAlgPolicy> {
template <class _Iter>
using __value_type = typename iterator_traits<_Iter>::value_type;
// advance
template <class _Iter, class _Distance>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11

View File

@ -9,6 +9,7 @@
#ifndef _LIBCPP___ALGORITHM_RANGES_UNIQUE_H
#define _LIBCPP___ALGORITHM_RANGES_UNIQUE_H
#include <__algorithm/iterator_operations.h>
#include <__algorithm/make_projected.h>
#include <__algorithm/unique.h>
#include <__config>
@ -37,28 +38,31 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace ranges {
namespace __unique {
struct __fn {
struct __fn {
template <
permutable _Iter,
sentinel_for<_Iter> _Sent,
class _Proj = identity,
indirect_equivalence_relation<projected<_Iter, _Proj>> _Comp = ranges::equal_to>
_LIBCPP_HIDE_FROM_ABI constexpr subrange<_Iter>
operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const {
auto __ret = std::__unique<_RangeAlgPolicy>(
std::move(__first), std::move(__last), ranges::__make_projected_comp(__comp, __proj));
return {std::move(__ret.first), std::move(__ret.second)};
}
template <permutable _Iter, sentinel_for<_Iter> _Sent, class _Proj = identity,
indirect_equivalence_relation<projected<_Iter, _Proj>> _Comp = ranges::equal_to>
_LIBCPP_HIDE_FROM_ABI constexpr
subrange<_Iter> operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const {
// TODO: implement
(void)__first; (void)__last; (void)__comp; (void)__proj;
return {};
}
template <forward_range _Range, class _Proj = identity,
indirect_equivalence_relation<projected<iterator_t<_Range>, _Proj>> _Comp = ranges::equal_to>
requires permutable<iterator_t<_Range>>
_LIBCPP_HIDE_FROM_ABI constexpr
borrowed_subrange_t<_Range> operator()(_Range&& __range, _Comp __comp = {}, _Proj __proj = {}) const {
// TODO: implement
(void)__range; (void)__comp; (void)__proj;
return {};
}
};
template <
forward_range _Range,
class _Proj = identity,
indirect_equivalence_relation<projected<iterator_t<_Range>, _Proj>> _Comp = ranges::equal_to>
requires permutable<iterator_t<_Range>>
_LIBCPP_HIDE_FROM_ABI constexpr borrowed_subrange_t<_Range>
operator()(_Range&& __range, _Comp __comp = {}, _Proj __proj = {}) const {
auto __ret = std::__unique<_RangeAlgPolicy>(
ranges::begin(__range), ranges::end(__range), ranges::__make_projected_comp(__comp, __proj));
return {std::move(__ret.first), std::move(__ret.second)};
}
};
} // namespace __unique

View File

@ -10,6 +10,7 @@
#define _LIBCPP___ALGORITHM_RANGES_UNIQUE_COPY_H
#include <__algorithm/in_out_result.h>
#include <__algorithm/iterator_operations.h>
#include <__algorithm/make_projected.h>
#include <__algorithm/unique_copy.h>
#include <__concepts/same_as.h>
@ -19,8 +20,8 @@
#include <__functional/ranges_operations.h>
#include <__iterator/concepts.h>
#include <__iterator/iterator_traits.h>
#include <__iterator/readable_traits.h>
#include <__iterator/projected.h>
#include <__iterator/readable_traits.h>
#include <__ranges/access.h>
#include <__ranges/concepts.h>
#include <__ranges/dangling.h>
@ -42,42 +43,68 @@ using unique_copy_result = in_out_result<_InIter, _OutIter>;
namespace __unique_copy {
template <class _InIter, class _OutIter>
concept __can_reread_from_output = (input_iterator<_OutIter> && same_as<iter_value_t<_InIter>, iter_value_t<_OutIter>>);
struct __fn {
template <class _InIter, class _OutIter>
static consteval auto __get_algo_tag() {
if constexpr (forward_iterator<_InIter>) {
return __unique_copy_tags::__reread_from_input_tag{};
} else if constexpr (__can_reread_from_output<_InIter, _OutIter>) {
return __unique_copy_tags::__reread_from_output_tag{};
} else if constexpr (indirectly_copyable_storable<_InIter, _OutIter>) {
return __unique_copy_tags::__read_from_tmp_value_tag{};
}
}
template <input_iterator _InIter, sentinel_for<_InIter> _Sent, weakly_incrementable _OutIter, class _Proj = identity,
template <class _InIter, class _OutIter>
using __algo_tag_t = decltype(__get_algo_tag<_InIter, _OutIter>());
template <input_iterator _InIter,
sentinel_for<_InIter> _Sent,
weakly_incrementable _OutIter,
class _Proj = identity,
indirect_equivalence_relation<projected<_InIter, _Proj>> _Comp = ranges::equal_to>
requires indirectly_copyable<_InIter, _OutIter> &&
(forward_iterator<_InIter> ||
(input_iterator<_OutIter> && same_as<iter_value_t<_InIter>, iter_value_t<_OutIter>>) ||
indirectly_copyable_storable<_InIter, _OutIter>)
_LIBCPP_HIDE_FROM_ABI constexpr
unique_copy_result<_InIter, _OutIter>
requires indirectly_copyable<_InIter, _OutIter> &&
(forward_iterator<_InIter> ||
(input_iterator<_OutIter> && same_as<iter_value_t<_InIter>, iter_value_t<_OutIter>>) ||
indirectly_copyable_storable<_InIter, _OutIter>)
_LIBCPP_HIDE_FROM_ABI constexpr unique_copy_result<_InIter, _OutIter>
operator()(_InIter __first, _Sent __last, _OutIter __result, _Comp __comp = {}, _Proj __proj = {}) const {
// TODO: implement
(void)__first; (void)__last; (void)__result; (void)__comp; (void)__proj;
return {};
auto __ret = std::__unique_copy<_RangeAlgPolicy>(
std::move(__first),
std::move(__last),
std::move(__result),
__make_projected_comp(__comp, __proj),
__algo_tag_t<_InIter, _OutIter>());
return {std::move(__ret.first), std::move(__ret.second)};
}
template <input_range _Range, weakly_incrementable _OutIter, class _Proj = identity,
template <input_range _Range,
weakly_incrementable _OutIter,
class _Proj = identity,
indirect_equivalence_relation<projected<iterator_t<_Range>, _Proj>> _Comp = ranges::equal_to>
requires indirectly_copyable<iterator_t<_Range>, _OutIter> &&
(forward_iterator<iterator_t<_Range>> ||
(input_iterator<_OutIter> && same_as<range_value_t<_Range>, iter_value_t<_OutIter>>) ||
indirectly_copyable_storable<iterator_t<_Range>, _OutIter>)
_LIBCPP_HIDE_FROM_ABI constexpr
unique_copy_result<borrowed_iterator_t<_Range>, _OutIter>
requires indirectly_copyable<iterator_t<_Range>, _OutIter> &&
(forward_iterator<iterator_t<_Range>> ||
(input_iterator<_OutIter> && same_as<range_value_t<_Range>, iter_value_t<_OutIter>>) ||
indirectly_copyable_storable<iterator_t<_Range>, _OutIter>)
_LIBCPP_HIDE_FROM_ABI constexpr unique_copy_result<borrowed_iterator_t<_Range>, _OutIter>
operator()(_Range&& __range, _OutIter __result, _Comp __comp = {}, _Proj __proj = {}) const {
// TODO: implement
(void)__range; (void)__result; (void)__comp; (void)__proj;
return {};
auto __ret = std::__unique_copy<_RangeAlgPolicy>(
ranges::begin(__range),
ranges::end(__range),
std::move(__result),
__make_projected_comp(__comp, __proj),
__algo_tag_t<iterator_t<_Range>, _OutIter>());
return {std::move(__ret.first), std::move(__ret.second)};
}
};
} // namespace __unique_copy
inline namespace __cpo {
inline constexpr auto unique_copy = __unique_copy::__fn{};
inline constexpr auto unique_copy = __unique_copy::__fn{};
} // namespace __cpo
} // namespace ranges

View File

@ -11,9 +11,11 @@
#include <__algorithm/adjacent_find.h>
#include <__algorithm/comp.h>
#include <__algorithm/iterator_operations.h>
#include <__config>
#include <__iterator/iterator_traits.h>
#include <__utility/move.h>
#include <__utility/pair.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@ -23,32 +25,34 @@ _LIBCPP_BEGIN_NAMESPACE_STD
// unique
template <class _AlgPolicy, class _Iter, class _Sent, class _BinaryPredicate>
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 std::pair<_Iter, _Iter>
__unique(_Iter __first, _Sent __last, _BinaryPredicate&& __pred) {
__first = std::__adjacent_find(__first, __last, __pred);
if (__first != __last) {
// ... a a ? ...
// f i
_Iter __i = __first;
for (++__i; ++__i != __last;)
if (!__pred(*__first, *__i))
*++__first = _IterOps<_AlgPolicy>::__iter_move(__i);
++__first;
return std::pair<_Iter, _Iter>(std::move(__first), std::move(__i));
}
return std::pair<_Iter, _Iter>(__first, __first);
}
template <class _ForwardIterator, class _BinaryPredicate>
_LIBCPP_NODISCARD_EXT _LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator
unique(_ForwardIterator __first, _ForwardIterator __last, _BinaryPredicate __pred)
{
__first = _VSTD::adjacent_find<_ForwardIterator, _BinaryPredicate&>(__first, __last, __pred);
if (__first != __last)
{
// ... a a ? ...
// f i
_ForwardIterator __i = __first;
for (++__i; ++__i != __last;)
if (!__pred(*__first, *__i))
*++__first = _VSTD::move(*__i);
++__first;
}
return __first;
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator
unique(_ForwardIterator __first, _ForwardIterator __last, _BinaryPredicate __pred) {
return std::__unique<_ClassicAlgPolicy>(std::move(__first), std::move(__last), __pred).first;
}
template <class _ForwardIterator>
_LIBCPP_NODISCARD_EXT inline
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
_ForwardIterator
unique(_ForwardIterator __first, _ForwardIterator __last)
{
typedef typename iterator_traits<_ForwardIterator>::value_type __v;
return _VSTD::unique(__first, __last, __equal_to<__v>());
_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator
unique(_ForwardIterator __first, _ForwardIterator __last) {
typedef typename iterator_traits<_ForwardIterator>::value_type __v;
return std::unique(__first, __last, __equal_to<__v>());
}
_LIBCPP_END_NAMESPACE_STD

View File

@ -10,8 +10,14 @@
#define _LIBCPP___ALGORITHM_UNIQUE_COPY_H
#include <__algorithm/comp.h>
#include <__algorithm/iterator_operations.h>
#include <__config>
#include <__iterator/iterator_traits.h>
#include <__type_traits/conditional.h>
#include <__type_traits/is_base_of.h>
#include <__type_traits/is_same.h>
#include <__utility/move.h>
#include <__utility/pair.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@ -19,88 +25,99 @@
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _BinaryPredicate, class _InputIterator, class _OutputIterator>
_LIBCPP_CONSTEXPR_AFTER_CXX17 _OutputIterator
__unique_copy(_InputIterator __first, _InputIterator __last, _OutputIterator __result, _BinaryPredicate __pred,
input_iterator_tag, output_iterator_tag)
{
if (__first != __last)
{
typename iterator_traits<_InputIterator>::value_type __t(*__first);
namespace __unique_copy_tags {
struct __reread_from_input_tag {};
struct __reread_from_output_tag {};
struct __read_from_tmp_value_tag {};
} // namespace __unique_copy_tags
template <class _AlgPolicy, class _BinaryPredicate, class _InputIterator, class _Sent, class _OutputIterator>
_LIBCPP_CONSTEXPR_AFTER_CXX17 _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _OutputIterator>
__unique_copy(_InputIterator __first,
_Sent __last,
_OutputIterator __result,
_BinaryPredicate&& __pred,
__unique_copy_tags::__read_from_tmp_value_tag) {
if (__first != __last) {
typename _IterOps<_AlgPolicy>::template __value_type<_InputIterator> __t(*__first);
*__result = __t;
++__result;
while (++__first != __last) {
if (!__pred(__t, *__first)) {
__t = *__first;
*__result = __t;
++__result;
while (++__first != __last)
{
if (!__pred(__t, *__first))
{
__t = *__first;
*__result = __t;
++__result;
}
}
}
}
return __result;
}
return pair<_InputIterator, _OutputIterator>(std::move(__first), std::move(__result));
}
template <class _BinaryPredicate, class _ForwardIterator, class _OutputIterator>
_LIBCPP_CONSTEXPR_AFTER_CXX17 _OutputIterator
__unique_copy(_ForwardIterator __first, _ForwardIterator __last, _OutputIterator __result, _BinaryPredicate __pred,
forward_iterator_tag, output_iterator_tag)
{
if (__first != __last)
{
_ForwardIterator __i = __first;
*__result = *__i;
++__result;
while (++__first != __last)
{
if (!__pred(*__i, *__first))
{
*__result = *__first;
++__result;
__i = __first;
}
}
}
return __result;
}
template <class _BinaryPredicate, class _InputIterator, class _ForwardIterator>
_LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator
__unique_copy(_InputIterator __first, _InputIterator __last, _ForwardIterator __result, _BinaryPredicate __pred,
input_iterator_tag, forward_iterator_tag)
{
if (__first != __last)
{
template <class _AlgPolicy, class _BinaryPredicate, class _ForwardIterator, class _Sent, class _OutputIterator>
_LIBCPP_CONSTEXPR_AFTER_CXX17 _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator, _OutputIterator>
__unique_copy(_ForwardIterator __first,
_Sent __last,
_OutputIterator __result,
_BinaryPredicate&& __pred,
__unique_copy_tags::__reread_from_input_tag) {
if (__first != __last) {
_ForwardIterator __i = __first;
*__result = *__i;
++__result;
while (++__first != __last) {
if (!__pred(*__i, *__first)) {
*__result = *__first;
while (++__first != __last)
if (!__pred(*__result, *__first))
*++__result = *__first;
++__result;
__i = __first;
}
}
return __result;
}
return pair<_ForwardIterator, _OutputIterator>(std::move(__first), std::move(__result));
}
template <class _AlgPolicy, class _BinaryPredicate, class _InputIterator, class _Sent, class _InputAndOutputIterator>
_LIBCPP_CONSTEXPR_AFTER_CXX17 _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _InputAndOutputIterator>
__unique_copy(_InputIterator __first,
_Sent __last,
_InputAndOutputIterator __result,
_BinaryPredicate&& __pred,
__unique_copy_tags::__reread_from_output_tag) {
if (__first != __last) {
*__result = *__first;
while (++__first != __last)
if (!__pred(*__result, *__first))
*++__result = *__first;
++__result;
}
return pair<_InputIterator, _InputAndOutputIterator>(std::move(__first), std::move(__result));
}
template <class _InputIterator, class _OutputIterator, class _BinaryPredicate>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
_OutputIterator
unique_copy(_InputIterator __first, _InputIterator __last, _OutputIterator __result, _BinaryPredicate __pred)
{
return _VSTD::__unique_copy<_BinaryPredicate&>(__first, __last, __result, __pred,
typename iterator_traits<_InputIterator>::iterator_category(),
typename iterator_traits<_OutputIterator>::iterator_category());
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 _OutputIterator
unique_copy(_InputIterator __first, _InputIterator __last, _OutputIterator __result, _BinaryPredicate __pred) {
using __algo_tag = typename conditional<
is_base_of<forward_iterator_tag, typename iterator_traits<_InputIterator>::iterator_category>::value,
__unique_copy_tags::__reread_from_input_tag,
typename conditional<
is_base_of<forward_iterator_tag, typename iterator_traits<_OutputIterator>::iterator_category>::value &&
is_same< typename iterator_traits<_InputIterator>::value_type,
typename iterator_traits<_OutputIterator>::value_type>::value,
__unique_copy_tags::__reread_from_output_tag,
__unique_copy_tags::__read_from_tmp_value_tag>::type >::type;
return std::__unique_copy<_ClassicAlgPolicy>(
std::move(__first), std::move(__last), std::move(__result), __pred, __algo_tag())
.second;
}
template <class _InputIterator, class _OutputIterator>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
_OutputIterator
unique_copy(_InputIterator __first, _InputIterator __last, _OutputIterator __result)
{
typedef typename iterator_traits<_InputIterator>::value_type __v;
return _VSTD::unique_copy(__first, __last, __result, __equal_to<__v>());
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 _OutputIterator
unique_copy(_InputIterator __first, _InputIterator __last, _OutputIterator __result) {
typedef typename iterator_traits<_InputIterator>::value_type __v;
return std::unique_copy(std::move(__first), std::move(__last), std::move(__result), __equal_to<__v>());
}
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___ALGORITHM_UNIQUE_COPY_H

View File

@ -865,6 +865,34 @@ namespace ranges {
borrowed_iterator_t<R>
inplace_merge(R&& r, iterator_t<R> middle, Comp comp = {},
Proj proj = {}); // Since C++20
template<permutable I, sentinel_for<I> S, class Proj = identity,
indirect_equivalence_relation<projected<I, Proj>> C = ranges::equal_to>
constexpr subrange<I> unique(I first, S last, C comp = {}, Proj proj = {}); // Since C++20
template<forward_range R, class Proj = identity,
indirect_equivalence_relation<projected<iterator_t<R>, Proj>> C = ranges::equal_to>
requires permutable<iterator_t<R>>
constexpr borrowed_subrange_t<R>
unique(R&& r, C comp = {}, Proj proj = {}); // Since C++20
template<input_iterator I, sentinel_for<I> S, weakly_incrementable O, class Proj = identity,
indirect_equivalence_relation<projected<I, Proj>> C = ranges::equal_to>
requires indirectly_copyable<I, O> &&
(forward_iterator<I> ||
(input_iterator<O> && same_as<iter_value_t<I>, iter_value_t<O>>) ||
indirectly_copyable_storable<I, O>)
constexpr unique_copy_result<I, O>
unique_copy(I first, S last, O result, C comp = {}, Proj proj = {}); // Since C++20
template<input_range R, weakly_incrementable O, class Proj = identity,
indirect_equivalence_relation<projected<iterator_t<R>, Proj>> C = ranges::equal_to>
requires indirectly_copyable<iterator_t<R>, O> &&
(forward_iterator<iterator_t<R>> ||
(input_iterator<O> && same_as<range_value_t<R>, iter_value_t<O>>) ||
indirectly_copyable_storable<iterator_t<R>, O>)
constexpr unique_copy_result<borrowed_iterator_t<R>, O>
unique_copy(R&& r, O result, C comp = {}, Proj proj = {}); // Since C++20
}
constexpr bool // constexpr in C++20
@ -1665,6 +1693,8 @@ template <class BidirectionalIterator, class Compare>
#include <__algorithm/ranges_stable_sort.h>
#include <__algorithm/ranges_swap_ranges.h>
#include <__algorithm/ranges_transform.h>
#include <__algorithm/ranges_unique.h>
#include <__algorithm/ranges_unique_copy.h>
#include <__algorithm/ranges_upper_bound.h>
#include <__algorithm/remove.h>
#include <__algorithm/remove_copy.h>

View File

@ -222,10 +222,10 @@ constexpr bool all_the_algorithms()
(void)std::ranges::transform(a, first2, UnaryTransform(&copies)); assert(copies == 0);
(void)std::ranges::transform(first, mid, mid, last, first2, BinaryTransform(&copies)); assert(copies == 0);
(void)std::ranges::transform(a, b, first2, BinaryTransform(&copies)); assert(copies == 0);
//(void)std::ranges::unique(first, last, Equal(&copies)); assert(copies == 0);
//(void)std::ranges::unique(a, Equal(&copies)); assert(copies == 0);
//(void)std::ranges::unique_copy(first, last, first2, Equal(&copies)); assert(copies == 0);
//(void)std::ranges::unique_copy(a, first2, Equal(&copies)); assert(copies == 0);
(void)std::ranges::unique(first, last, Equal(&copies)); assert(copies == 0);
(void)std::ranges::unique(a, Equal(&copies)); assert(copies == 0);
(void)std::ranges::unique_copy(first, last, first2, Equal(&copies)); assert(copies == 0);
(void)std::ranges::unique_copy(a, first2, Equal(&copies)); assert(copies == 0);
(void)std::ranges::upper_bound(first, last, value, Less(&copies)); assert(copies == 0);
(void)std::ranges::upper_bound(a, value, Less(&copies)); assert(copies == 0);

View File

@ -213,10 +213,10 @@ constexpr bool all_the_algorithms()
(void)std::ranges::transform(a, first2, UnaryTransform(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::transform(first, mid, mid, last, first2, BinaryTransform(), Proj(&copies), Proj(&copies)); assert(copies == 0);
(void)std::ranges::transform(a, b, first2, BinaryTransform(), Proj(&copies), Proj(&copies)); assert(copies == 0);
//(void)std::ranges::unique(first, last, Equal(), Proj(&copies)); assert(copies == 0);
//(void)std::ranges::unique(a, Equal(), Proj(&copies)); assert(copies == 0);
//(void)std::ranges::unique_copy(first, last, first2, Equal(), Proj(&copies)); assert(copies == 0);
//(void)std::ranges::unique_copy(a, first2, Equal(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::unique(first, last, Equal(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::unique(a, Equal(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::unique_copy(first, last, first2, Equal(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::unique_copy(a, first2, Equal(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::upper_bound(first, last, value, Less(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::upper_bound(a, value, Less(), Proj(&copies)); assert(copies == 0);

View File

@ -28,14 +28,214 @@
#include <ranges>
#include "almost_satisfies_types.h"
#include "counting_predicates.h"
#include "counting_projection.h"
#include "test_iterators.h"
// TODO: SFINAE tests.
template <class Iter = int*, class Sent = int*, class Comp = std::ranges::equal_to, class Proj = std::identity>
concept HasUniqueIter =
requires(Iter&& iter, Sent&& sent, Comp&& comp, Proj&& proj) {
std::ranges::unique(
std::forward<Iter>(iter), std::forward<Sent>(sent), std::forward<Comp>(comp), std::forward<Proj>(proj));
};
static_assert(HasUniqueIter<int*, int*>);
// !permutable<I>
static_assert(!HasUniqueIter<PermutableNotForwardIterator>);
static_assert(!HasUniqueIter<PermutableNotSwappable>);
// !sentinel_for<S, I>
static_assert(!HasUniqueIter<int*, SentinelForNotSemiregular>);
// !indirect_equivalence_relation<Comp, projected<I, Proj>>
static_assert(!HasUniqueIter<int*, int*, ComparatorNotCopyable<int>>);
template <class Range, class Comp = std::ranges::equal_to, class Proj = std::identity>
concept HasUniqueRange =
requires(Range&& range, Comp&& comp, Proj&& proj) {
std::ranges::unique(std::forward<Range>(range), std::forward<Comp>(comp), std::forward<Proj>(proj));
};
template <class T>
using R = UncheckedRange<T>;
static_assert(HasUniqueRange<R<int*>>);
// !forward_range<R>
static_assert(!HasUniqueRange<ForwardRangeNotDerivedFrom>);
static_assert(!HasUniqueRange<ForwardRangeNotIncrementable>);
// permutable<ranges::iterator_t<R>>
static_assert(!HasUniqueRange<R<PermutableNotForwardIterator>>);
static_assert(!HasUniqueRange<R<PermutableNotSwappable>>);
// !indirect_equivalence_relation<Comp, projected<ranges::iterator_t<R>, Proj>>
static_assert(!HasUniqueRange<R<int*>, ComparatorNotCopyable<int>>);
template <class Iter, template <class> class SentWrapper, std::size_t N1, std::size_t N2>
constexpr void testUniqueImpl(std::array<int, N1> input, std::array<int, N2> expected) {
using Sent = SentWrapper<Iter>;
// iterator overload
{
auto in = input;
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result =
std::ranges::unique(Iter{in.data()}, Sent{Iter{in.data() + in.size()}});
assert(std::ranges::equal(std::ranges::subrange<Iter>{Iter{in.data()}, result.begin()}, expected));
assert(base(result.end()) == in.data() + in.size());
}
// range overload
{
auto in = input;
std::ranges::subrange r{Iter{in.data()}, Sent{Iter{in.data() + in.size()}}};
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::unique(r);
assert(std::ranges::equal(std::ranges::subrange<Iter>{Iter{in.data()}, result.begin()}, expected));
assert(base(result.end()) == in.data() + in.size());
}
}
template <class Iter, template <class> class SentWrapper>
constexpr void testImpl() {
// no consecutive elements
{
std::array in{1, 2, 3, 2, 1};
std::array expected{1, 2, 3, 2, 1};
testUniqueImpl<Iter, SentWrapper>(in, expected);
}
// one group of consecutive elements
{
std::array in{2, 3, 3, 3, 4, 3};
std::array expected{2, 3, 4, 3};
testUniqueImpl<Iter, SentWrapper>(in, expected);
}
// multiple groups of consecutive elements
{
std::array in{2, 3, 3, 3, 4, 3, 3, 5, 5, 5};
std::array expected{2, 3, 4, 3, 5};
testUniqueImpl<Iter, SentWrapper>(in, expected);
}
// all the same
{
std::array in{1, 1, 1, 1, 1, 1};
std::array expected{1};
testUniqueImpl<Iter, SentWrapper>(in, expected);
}
// empty range
{
std::array<int, 0> in{};
std::array<int, 0> expected{};
testUniqueImpl<Iter, SentWrapper>(in, expected);
}
// single element range
std::array in{1};
std::array expected{1};
testUniqueImpl<Iter, SentWrapper>(in, expected);
}
template <template <class> class SentWrapper>
constexpr void withAllPermutationsOfIter() {
testImpl<forward_iterator<int*>, SentWrapper>();
testImpl<bidirectional_iterator<int*>, SentWrapper>();
testImpl<random_access_iterator<int*>, SentWrapper>();
testImpl<contiguous_iterator<int*>, SentWrapper>();
testImpl<int*, SentWrapper>();
}
constexpr bool test() {
// TODO: main tests.
// TODO: A custom comparator works.
// TODO: A custom projection works.
withAllPermutationsOfIter<std::type_identity_t>();
withAllPermutationsOfIter<sentinel_wrapper>();
struct Data {
int data;
};
// Test custom comparator
{
std::array input{Data{4}, Data{8}, Data{8}, Data{8}};
std::array expected{Data{4}, Data{8}};
const auto comp = [](const Data& x, const Data& y) { return x.data == y.data; };
// iterator overload
{
auto in = input;
auto result = std::ranges::unique(in.begin(), in.end(), comp);
assert(std::ranges::equal(in.begin(), result.begin(), expected.begin(), expected.end(), comp));
assert(base(result.end()) == in.end());
}
// range overload
{
auto in = input;
auto result = std::ranges::unique(in, comp);
assert(std::ranges::equal(in.begin(), result.begin(), expected.begin(), expected.end(), comp));
assert(base(result.end()) == in.end());
}
}
// Test custom projection
{
std::array input{Data{4}, Data{8}, Data{8}, Data{8}};
std::array expected{Data{4}, Data{8}};
const auto proj = &Data::data;
// iterator overload
{
auto in = input;
auto result = std::ranges::unique(in.begin(), in.end(), {}, proj);
assert(std::ranges::equal(in.begin(), result.begin(), expected.begin(), expected.end(), {}, proj, proj));
assert(base(result.end()) == in.end());
}
// range overload
{
auto in = input;
auto result = std::ranges::unique(in, {}, proj);
assert(std::ranges::equal(in.begin(), result.begin(), expected.begin(), expected.end(), {}, proj, proj));
assert(base(result.end()) == in.end());
}
}
// Complexity: For nonempty ranges, exactly (last - first) - 1 applications of the corresponding predicate
// and no more than twice as many applications of any projection.
{
std::array input{1, 2, 3, 3, 3, 4, 3, 3, 5, 5, 6, 6, 1};
std::array expected{1, 2, 3, 4, 3, 5, 6, 1};
// iterator overload
{
auto in = input;
int numberOfComp = 0;
int numberOfProj = 0;
auto result = std::ranges::unique(
in.begin(),
in.end(),
counting_predicate{std::ranges::equal_to{}, numberOfComp},
counting_projection{numberOfProj});
assert(std::ranges::equal(in.begin(), result.begin(), expected.begin(), expected.end()));
assert(base(result.end()) == in.end());
assert(numberOfComp == in.size() - 1);
assert(numberOfProj <= static_cast<int>(2 * (in.size() - 1)));
}
// range overload
{
auto in = input;
int numberOfComp = 0;
int numberOfProj = 0;
auto result = std::ranges::unique(
in, counting_predicate{std::ranges::equal_to{}, numberOfComp}, counting_projection{numberOfProj});
assert(std::ranges::equal(in.begin(), result.begin(), expected.begin(), expected.end()));
assert(base(result.end()) == in.end());
assert(numberOfComp == in.size() - 1);
assert(numberOfProj <= static_cast<int>(2 * (in.size() - 1)));
}
}
return true;
}

View File

@ -36,15 +36,409 @@
#include <ranges>
#include "almost_satisfies_types.h"
#include "counting_predicates.h"
#include "counting_projection.h"
#include "MoveOnly.h"
#include "test_iterators.h"
// TODO: SFINAE tests.
template <
class InIter = int*,
class Sent = int*,
class OutIter = int*,
class Comp = std::ranges::equal_to,
class Proj = std::identity>
concept HasUniqueCopyIter =
requires(InIter&& in, Sent&& sent, OutIter&& out, Comp&& comp, Proj&& proj) {
std::ranges::unique_copy(
std::forward<InIter>(in),
std::forward<Sent>(sent),
std::forward<OutIter>(out),
std::forward<Comp>(comp),
std::forward<Proj>(proj));
};
static_assert(HasUniqueCopyIter<int*, int*, int*>);
// !input_iterator<I>
static_assert(!HasUniqueCopyIter<InputIteratorNotDerivedFrom, sentinel_wrapper<InputIteratorNotDerivedFrom>>);
// !sentinel_for<S, I>
static_assert(!HasUniqueCopyIter<int*, SentinelForNotSemiregular>);
// !weakly_incrementable<O>
static_assert(!HasUniqueCopyIter<int*, int*, WeaklyIncrementableNotMovable>);
// !indirect_equivalence_relation<Comp, projected<I, Proj>>
static_assert(!HasUniqueCopyIter<int*, int*, int*, ComparatorNotCopyable<int>>);
// !indirectly_copyable<I, O>
static_assert(!HasUniqueCopyIter<const int*, const int*, const int*>);
// forward_iterator<I>
// !(input_iterator<O> && same_as<iter_value_t<I>, iter_value_t<O>>)
// !indirectly_copyable_storable<I, O>
struct AssignableFromMoveOnly {
int data;
constexpr AssignableFromMoveOnly& operator=(MoveOnly const& m) {
data = m.get();
return *this;
}
};
static_assert(HasUniqueCopyIter<MoveOnly*, MoveOnly*, AssignableFromMoveOnly*>);
// because:
static_assert(std::forward_iterator<MoveOnly*>);
static_assert(!std::same_as<std::iter_value_t<MoveOnly*>, std::iter_value_t<AssignableFromMoveOnly*>>);
static_assert(!std::indirectly_copyable_storable<MoveOnly*, AssignableFromMoveOnly*>);
// !forward_iterator<I>
// (input_iterator<O> && same_as<iter_value_t<I>, iter_value_t<O>>)
// !indirectly_copyable_storable<I, O>
struct CopyAssignableNotCopyConstructible {
int data;
constexpr CopyAssignableNotCopyConstructible(int i = 0) : data(i) {}
CopyAssignableNotCopyConstructible(const CopyAssignableNotCopyConstructible&) = delete;
CopyAssignableNotCopyConstructible& operator=(const CopyAssignableNotCopyConstructible&) = default;
friend constexpr bool
operator==(CopyAssignableNotCopyConstructible const&, CopyAssignableNotCopyConstructible const&) = default;
};
using InputAndOutputIterator = cpp17_input_iterator<CopyAssignableNotCopyConstructible*>;
static_assert(std::input_iterator<InputAndOutputIterator>);
static_assert(std::output_iterator<InputAndOutputIterator, CopyAssignableNotCopyConstructible>);
static_assert(
HasUniqueCopyIter<
cpp20_input_iterator<CopyAssignableNotCopyConstructible*>,
sentinel_wrapper<cpp20_input_iterator<CopyAssignableNotCopyConstructible*>>,
InputAndOutputIterator>);
// because:
static_assert(!std::forward_iterator< cpp20_input_iterator<CopyAssignableNotCopyConstructible*>>);
static_assert(
std::input_iterator<InputAndOutputIterator> &&
std::same_as<std::iter_value_t<cpp20_input_iterator<CopyAssignableNotCopyConstructible*>>,
std::iter_value_t<InputAndOutputIterator>>);
static_assert(
!std::indirectly_copyable_storable<
cpp20_input_iterator<CopyAssignableNotCopyConstructible*>,
InputAndOutputIterator>);
// !forward_iterator<I>
// !(input_iterator<O> && same_as<iter_value_t<I>, iter_value_t<O>>)
// indirectly_copyable_storable<I, O>
static_assert(
HasUniqueCopyIter<
cpp20_input_iterator<int*>,
sentinel_wrapper<cpp20_input_iterator<int*>>,
cpp20_output_iterator<int*>>);
// because:
static_assert(!std::forward_iterator<cpp20_input_iterator<int*>>);
static_assert(!std::input_iterator<cpp20_output_iterator<int*>>);
static_assert(std::indirectly_copyable_storable<cpp20_input_iterator<int*>, cpp20_output_iterator<int*>>);
// !forward_iterator<I>
// !(input_iterator<O> && same_as<iter_value_t<I>, iter_value_t<O>>)
// !indirectly_copyable_storable<I, O>
static_assert(
!HasUniqueCopyIter<
cpp20_input_iterator<MoveOnly*>,
sentinel_wrapper<cpp20_input_iterator<MoveOnly*>>,
cpp20_output_iterator<AssignableFromMoveOnly*>>);
// because:
static_assert(!std::forward_iterator<cpp20_input_iterator<MoveOnly*>>);
static_assert(!std::input_iterator<cpp20_output_iterator<MoveOnly*>>);
static_assert(
!std::
indirectly_copyable_storable<cpp20_input_iterator<MoveOnly*>, cpp20_output_iterator<AssignableFromMoveOnly*>>);
template < class Range, class OutIter = int*, class Comp = std::ranges::equal_to, class Proj = std::identity>
concept HasUniqueCopyRange =
requires(Range&& range, OutIter&& out, Comp&& comp, Proj&& proj) {
std::ranges::unique_copy(
std::forward<Range>(range), std::forward<OutIter>(out), std::forward<Comp>(comp), std::forward<Proj>(proj));
};
template <class T>
using R = UncheckedRange<T>;
static_assert(HasUniqueCopyRange<R<int*>, int*>);
// !input_range<R>
static_assert(!HasUniqueCopyRange<R<InputIteratorNotDerivedFrom>>);
// !weakly_incrementable<O>
static_assert(!HasUniqueCopyIter<R<int*>, WeaklyIncrementableNotMovable>);
// !indirect_equivalence_relation<Comp, projected<I, Proj>>
static_assert(!HasUniqueCopyIter<R<int*>, int*, ComparatorNotCopyable<int>>);
// !indirectly_copyable<I, O>
static_assert(!HasUniqueCopyIter<R<const int*>, const int*>);
// !forward_iterator<iterator_t<R>>
// !(input_iterator<O> && same_as<range_value_t<R>, iter_value_t<O>>)
// !indirectly_copyable_storable<iterator_t<R>, O>
static_assert(!HasUniqueCopyIter< R<cpp20_input_iterator<MoveOnly*>>, cpp20_output_iterator<AssignableFromMoveOnly*>>);
template <class InIter, class OutIter, template <class> class SentWrapper, std::size_t N1, std::size_t N2>
constexpr void testUniqueCopyImpl(std::array<int, N1> in, std::array<int, N2> expected) {
using Sent = SentWrapper<InIter>;
// iterator overload
{
std::array<int, N2> out;
std::same_as<std::ranges::unique_copy_result<InIter, OutIter>> decltype(auto) result =
std::ranges::unique_copy(InIter{in.data()}, Sent{InIter{in.data() + in.size()}}, OutIter{out.begin()});
assert(std::ranges::equal(out, expected));
assert(base(result.in) == in.data() + in.size());
assert(base(result.out) == out.data() + out.size());
}
// range overload
{
std::array<int, N2> out;
std::ranges::subrange r{InIter{in.data()}, Sent{InIter{in.data() + in.size()}}};
std::same_as<std::ranges::unique_copy_result<InIter, OutIter>> decltype(auto) result =
std::ranges::unique_copy(r, OutIter{out.begin()});
assert(std::ranges::equal(out, expected));
assert(base(result.in) == in.data() + in.size());
assert(base(result.out) == out.data() + out.size());
}
}
template <class InIter, class OutIter, template <class> class SentWrapper>
constexpr void testImpl() {
// no consecutive elements
{
std::array in{1, 2, 3, 2, 1};
std::array expected{1, 2, 3, 2, 1};
testUniqueCopyImpl<InIter, OutIter, SentWrapper>(in, expected);
}
// one group of consecutive elements
{
std::array in{2, 3, 3, 3, 4, 3};
std::array expected{2, 3, 4, 3};
testUniqueCopyImpl<InIter, OutIter, SentWrapper>(in, expected);
}
// multiple groups of consecutive elements
{
std::array in{2, 3, 3, 3, 4, 3, 3, 5, 5, 5};
std::array expected{2, 3, 4, 3, 5};
testUniqueCopyImpl<InIter, OutIter, SentWrapper>(in, expected);
}
// all the same
{
std::array in{1, 1, 1, 1, 1, 1};
std::array expected{1};
testUniqueCopyImpl<InIter, OutIter, SentWrapper>(in, expected);
}
// empty range
{
std::array<int, 0> in{};
std::array<int, 0> expected{};
testUniqueCopyImpl<InIter, OutIter, SentWrapper>(in, expected);
}
}
template <class OutIter, template <class> class SentWrapper>
constexpr void withAllPermutationsOfInIter() {
testImpl<cpp20_input_iterator<int*>, OutIter, sentinel_wrapper>();
testImpl<forward_iterator<int*>, OutIter, SentWrapper>();
testImpl<bidirectional_iterator<int*>, OutIter, SentWrapper>();
testImpl<random_access_iterator<int*>, OutIter, SentWrapper>();
testImpl<contiguous_iterator<int*>, OutIter, SentWrapper>();
testImpl<int*, OutIter, SentWrapper>();
}
template <template <class> class SentWrapper>
constexpr void withAllPermutationsOfInIterAndOutIter() {
withAllPermutationsOfInIter<cpp20_output_iterator<int*>, SentWrapper>();
withAllPermutationsOfInIter<forward_iterator<int*>, SentWrapper>();
withAllPermutationsOfInIter<bidirectional_iterator<int*>, SentWrapper>();
withAllPermutationsOfInIter<random_access_iterator<int*>, SentWrapper>();
withAllPermutationsOfInIter<contiguous_iterator<int*>, SentWrapper>();
withAllPermutationsOfInIter<int*, SentWrapper>();
}
constexpr bool test() {
// TODO: main tests.
// TODO: A custom comparator works.
// TODO: A custom projection works.
withAllPermutationsOfInIterAndOutIter<std::type_identity_t>();
withAllPermutationsOfInIterAndOutIter<sentinel_wrapper>();
// Test the overload that re-reads from the input iterator
// forward_iterator<I>
// !(input_iterator<O> && same_as<iter_value_t<I>, iter_value_t<O>>)
// !indirectly_copyable_storable<I, O>
{
MoveOnly in[5] = {1, 3, 3, 3, 1};
// iterator overload
{
AssignableFromMoveOnly out[3] = {};
auto result = std::ranges::unique_copy(in, in + 5, out);
assert(std::ranges::equal(out, std::array{1, 3, 1}, {}, &AssignableFromMoveOnly::data));
assert(result.in == in + 5);
assert(result.out == out + 3);
}
// range overload
{
AssignableFromMoveOnly out[3] = {};
auto result = std::ranges::unique_copy(std::ranges::subrange{in, in + 5}, out);
assert(std::ranges::equal(out, std::array{1, 3, 1}, {}, &AssignableFromMoveOnly::data));
assert(result.in == in + 5);
assert(result.out == out + 3);
}
}
// Test the overload that re-reads from the output iterator
// !forward_iterator<I>
// (input_iterator<O> && same_as<iter_value_t<I>, iter_value_t<O>>)
// !indirectly_copyable_storable<I, O>
{
using InIter = cpp20_input_iterator<CopyAssignableNotCopyConstructible*>;
using Sent = sentinel_wrapper<InIter>;
CopyAssignableNotCopyConstructible in[6] = {1, 1, 2, 2, 3, 3};
// iterator overload
{
CopyAssignableNotCopyConstructible out[3];
auto result = std::ranges::unique_copy(InIter{in}, Sent{InIter{in + 6}}, InputAndOutputIterator{out});
assert(std::ranges::equal(out, std::array{1, 2, 3}, {}, &CopyAssignableNotCopyConstructible::data));
assert(base(result.in) == in + 6);
assert(base(result.out) == out + 3);
}
// range overload
{
CopyAssignableNotCopyConstructible out[3];
auto r = std::ranges::subrange(InIter{in}, Sent{InIter{in + 6}});
auto result = std::ranges::unique_copy(r, InputAndOutputIterator{out});
assert(std::ranges::equal(out, std::array{1, 2, 3}, {}, &CopyAssignableNotCopyConstructible::data));
assert(base(result.in) == in + 6);
assert(base(result.out) == out + 3);
}
}
// Test the overload that reads from the temporary copy of the value
// !forward_iterator<I>
// !(input_iterator<O> && same_as<iter_value_t<I>, iter_value_t<O>>)
// indirectly_copyable_storable<I, O>
{
using InIter = cpp20_input_iterator<int*>;
using Sent = sentinel_wrapper<InIter>;
int in[4] = {1, 1, 1, 2};
// iterator overload
{
int out[2];
auto result = std::ranges::unique_copy(InIter{in}, Sent{InIter{in + 4}}, cpp20_output_iterator<int*>{out});
assert(std::ranges::equal(out, std::array{1, 2}));
assert(base(result.in) == in + 4);
assert(base(result.out) == out + 2);
}
// range overload
{
int out[2];
auto r = std::ranges::subrange(InIter{in}, Sent{InIter{in + 4}});
auto result = std::ranges::unique_copy(r, cpp20_output_iterator<int*>{out});
assert(std::ranges::equal(out, std::array{1, 2}));
assert(base(result.in) == in + 4);
assert(base(result.out) == out + 2);
}
}
struct Data {
int data;
};
// Test custom comparator
{
std::array in{Data{4}, Data{8}, Data{8}, Data{8}};
std::array expected{Data{4}, Data{8}};
const auto comp = [](const Data& x, const Data& y) { return x.data == y.data; };
// iterator overload
{
std::array<Data, 2> out;
auto result = std::ranges::unique_copy(in.begin(), in.end(), out.begin(), comp);
assert(std::ranges::equal(out, expected, comp));
assert(base(result.in) == in.begin() + 4);
assert(base(result.out) == out.begin() + 2);
}
// range overload
{
std::array<Data, 2> out;
auto result = std::ranges::unique_copy(in, out.begin(), comp);
assert(std::ranges::equal(out, expected, comp));
assert(base(result.in) == in.begin() + 4);
assert(base(result.out) == out.begin() + 2);
}
}
// Test custom projection
{
std::array in{Data{4}, Data{8}, Data{8}, Data{8}};
std::array expected{Data{4}, Data{8}};
const auto proj = &Data::data;
// iterator overload
{
std::array<Data, 2> out;
auto result = std::ranges::unique_copy(in.begin(), in.end(), out.begin(), {}, proj);
assert(std::ranges::equal(out, expected, {}, proj, proj));
assert(base(result.in) == in.begin() + 4);
assert(base(result.out) == out.begin() + 2);
}
// range overload
{
std::array<Data, 2> out;
auto result = std::ranges::unique_copy(in, out.begin(), {}, proj);
assert(std::ranges::equal(out, expected, {}, proj, proj));
assert(base(result.in) == in.begin() + 4);
assert(base(result.out) == out.begin() + 2);
}
}
// Exactly last - first - 1 applications of the corresponding predicate and no
// more than twice as many applications of any projection.
{
std::array in{1, 2, 3, 3, 3, 4, 3, 3, 5, 5, 6, 6, 1};
std::array expected{1, 2, 3, 4, 3, 5, 6, 1};
// iterator overload
{
std::array<int, 8> out;
int numberOfComp = 0;
int numberOfProj = 0;
auto result = std::ranges::unique_copy(
in.begin(),
in.end(),
out.begin(),
counting_predicate{std::ranges::equal_to{}, numberOfComp},
counting_projection{numberOfProj});
assert(std::ranges::equal(out, expected));
assert(base(result.in) == in.end());
assert(base(result.out) == out.end());
assert(numberOfComp == in.size() - 1);
assert(numberOfProj <= static_cast<int>(2 * (in.size() - 1)));
}
// range overload
{
std::array<int, 8> out;
int numberOfComp = 0;
int numberOfProj = 0;
auto result = std::ranges::unique_copy(
in,
out.begin(),
counting_predicate{std::ranges::equal_to{}, numberOfComp},
counting_projection{numberOfProj});
assert(std::ranges::equal(out, expected));
assert(base(result.in) == in.end());
assert(base(result.out) == out.end());
assert(numberOfComp == in.size() - 1);
assert(numberOfProj <= static_cast<int>(2 * (in.size() - 1)));
}
}
return true;
}

View File

@ -19,9 +19,21 @@
#include <algorithm>
#include <cassert>
#include "MoveOnly.h"
#include "test_macros.h"
#include "test_iterators.h"
struct AssignableFromMoveOnly {
AssignableFromMoveOnly(int i) : data(i) {}
AssignableFromMoveOnly() : data(0) {}
int data;
AssignableFromMoveOnly& operator=(MoveOnly const& m) {
data = m.get();
return *this;
}
bool operator==(AssignableFromMoveOnly const& rhs) const { return data == rhs.data; }
};
#if TEST_STD_VER > 17
TEST_CONSTEXPR bool test_constexpr() {
int ia[] = {0, 1, 2, 2, 4};
@ -107,6 +119,7 @@ test()
int main(int, char**)
{
test<cpp17_input_iterator<const int*>, cpp17_input_iterator<int*> >();
test<cpp17_input_iterator<const int*>, cpp17_output_iterator<int*> >();
test<cpp17_input_iterator<const int*>, forward_iterator<int*> >();
test<cpp17_input_iterator<const int*>, bidirectional_iterator<int*> >();
@ -137,6 +150,16 @@ int main(int, char**)
test<const int*, random_access_iterator<int*> >();
test<const int*, int*>();
// Move only inputs
{
MoveOnly in[5] = {1, 3, 3, 3, 1};
AssignableFromMoveOnly out[3] = {};
auto result = std::unique_copy(in, in + 5, out);
AssignableFromMoveOnly expected[3] = {1, 3, 1};
assert(std::equal(out, out + 3, expected));
assert(result == out + 3);
}
#if TEST_STD_VER > 17
static_assert(test_constexpr());
#endif

View File

@ -43,7 +43,7 @@ static_assert(std::is_same_v<in_out_result<int, long>, uninitialized_copy_result
static_assert(std::is_same_v<in_out_result<int, long>, uninitialized_copy_n_result<int, long>>);
static_assert(std::is_same_v<in_out_result<int, long>, uninitialized_move_result<int, long>>);
static_assert(std::is_same_v<in_out_result<int, long>, uninitialized_move_n_result<int, long>>);
// static_assert(std::is_same_v<in_out_result<int, long>, unique_copy_result<int, long>>);
static_assert(std::is_same_v<in_out_result<int, long>, unique_copy_result<int, long>>);
static_assert(std::is_same_v<in_in_out_result<int, long, char>, binary_transform_result<int, long, char>>);
static_assert(std::is_same_v<in_in_out_result<int, long, char>, merge_result<int, long, char>>);

View File

@ -88,7 +88,7 @@ constexpr bool test_all() {
using std::ranges::set_union_result;
using std::ranges::swap_ranges_result;
using std::ranges::unary_transform_result;
//using std::ranges::unique_copy_result;
using std::ranges::unique_copy_result;
auto unary_pred = [](int i) { return i > 0; };
auto binary_pred = [](int i, int j) { return i < j; };
@ -117,7 +117,7 @@ constexpr bool test_all() {
dangling_1st(std::ranges::partition_point, in, unary_pred);
dangling_1st(std::ranges::lower_bound, in, x);
dangling_1st(std::ranges::upper_bound, in, x);
//dangling_1st(std::ranges::equal_range, in, x);
dangling_1st(std::ranges::equal_range, in, x);
dangling_1st(std::ranges::min_element, in);
dangling_1st(std::ranges::max_element, in);
dangling_1st<minmax_result<dangling>>(std::ranges::minmax_element, in);
@ -157,7 +157,7 @@ constexpr bool test_all() {
dangling_both<swap_ranges_result<dangling, dangling>>(std::ranges::swap_ranges, in, in2);
dangling_1st<reverse_copy_result<dangling, int*>>(std::ranges::reverse_copy, in, out);
dangling_1st<rotate_copy_result<dangling, int*>>(std::ranges::rotate_copy, in, mid, out);
//dangling_1st<unique_copy_result<dangling, int*>>(std::ranges::unique_copy, in, out);
dangling_1st<unique_copy_result<dangling, int*>>(std::ranges::unique_copy, in, out);
dangling_1st<partition_copy_result<dangling, int*, int*>>(std::ranges::partition_copy, in, out, out2, unary_pred);
//dangling_1st<partial_sort_copy_result<dangling, int*>>(std::ranges::partial_sort_copy, in, in2);
//dangling_2nd<partial_sort_copy_result<int*, dangling>>(std::ranges::partial_sort_copy, in, in2);
@ -184,7 +184,7 @@ constexpr bool test_all() {
//dangling_1st(std::ranges::rotate, in, mid);
if (!std::is_constant_evaluated()) // `shuffle` isn't `constexpr`.
dangling_1st(std::ranges::shuffle, in, rand_gen());
//dangling_1st(std::ranges::unique, in);
dangling_1st(std::ranges::unique, in);
dangling_1st(std::ranges::partition, in, unary_pred);
if (!std::is_constant_evaluated())
dangling_1st(std::ranges::stable_partition, in, unary_pred);

View File

@ -115,7 +115,7 @@ constexpr bool test_all() {
//test(std::ranges::remove_copy_if, in, out, unary_pred);
test(std::ranges::replace_if, in, unary_pred, x);
//test(std::ranges::replace_copy_if, in, out, unary_pred, x);
//test(std::ranges::unique_copy, in, out, binary_pred);
test(std::ranges::unique_copy, in, out, binary_pred);
test(std::ranges::partition_copy, in, out, out2, unary_pred);
//test(std::ranges::partial_sort_copy, in, in2, binary_pred);
test(std::ranges::merge, in, in2, out, binary_pred);
@ -124,7 +124,7 @@ constexpr bool test_all() {
test(std::ranges::set_symmetric_difference, in, in2, out, binary_pred);
test(std::ranges::set_union, in, in2, out, binary_pred);
test(std::ranges::remove_if, in, unary_pred);
//test(std::ranges::unique, in, binary_pred);
test(std::ranges::unique, in, binary_pred);
test(std::ranges::partition, in, unary_pred);
if (!std::is_constant_evaluated())
test(std::ranges::stable_partition, in, unary_pred);

View File

@ -140,7 +140,7 @@ constexpr bool test_all() {
// `reverse_copy` has neither a projection nor a predicate.
// `rotate_copy` has neither a projection nor a predicate.
// `sample` has no requirement that the given generator be invoked via `std::invoke`.
//test(std::ranges::unique_copy, in, out, &Foo::binary_pred, &Bar::val);
test(std::ranges::unique_copy, in, out, &Foo::binary_pred, &Bar::val);
test(std::ranges::partition_copy, in, out, out2, &Foo::unary_pred, &Bar::val);
//test(std::ranges::partial_sort_copy, in, in2, &Foo::binary_pred, &Bar::val);
test(std::ranges::merge, in, in2, out, &Foo::binary_pred, &Bar::val, &Bar::val);
@ -153,7 +153,7 @@ constexpr bool test_all() {
// `reverse` has neither a projection nor a predicate.
// `rotate` has neither a projection nor a predicate.
// `shuffle` has neither a projection nor a predicate.
//test(std::ranges::unique, in, &Foo::binary_pred, &Bar::val);
test(std::ranges::unique, in, &Foo::binary_pred, &Bar::val);
test(std::ranges::partition, in, &Foo::unary_pred, &Bar::val);
if (!std::is_constant_evaluated())
test(std::ranges::stable_partition, in, &Foo::unary_pred, &Bar::val);

View File

@ -136,7 +136,7 @@ constexpr void run_tests() {
if constexpr (std::copyable<T>) {
test(std::ranges::reverse_copy, in, out);
test_mid(std::ranges::rotate_copy, in, mid, out);
//test(std::ranges::unique_copy, in, out);
test(std::ranges::unique_copy, in, out);
test(std::ranges::partition_copy, in, out, out2, unary_pred);
//test_mid(std::ranges::partial_sort_copy, in, in2);
test(std::ranges::merge, in, in2, out);
@ -153,7 +153,7 @@ constexpr void run_tests() {
test(std::ranges::shuffle, in, rand_gen());
//if (!std::is_constant_evaluated())
// test(std::ranges::sample, in, out, count, rand_gen());
//test(std::ranges::unique, in);
test(std::ranges::unique, in);
test(std::ranges::partition, in, unary_pred);
// TODO(ranges): `stable_partition` requires `ranges::rotate` to be implemented.
//if (!std::is_constant_evaluated())

View File

@ -145,8 +145,8 @@ static_assert(test(std::ranges::stable_sort, a));
//static_assert(test(std::ranges::starts_with, a, a));
static_assert(test(std::ranges::swap_ranges, a, a));
static_assert(test(std::ranges::transform, a, a, triple));
//static_assert(test(std::ranges::unique, a));
//static_assert(test(std::ranges::unique_copy, a, a));
static_assert(test(std::ranges::unique, a));
static_assert(test(std::ranges::unique_copy, a, a));
static_assert(test(std::ranges::upper_bound, a, 42));
// [memory.syn]