aboutsummaryrefslogtreecommitdiff
path: root/libcxx/test/std
diff options
context:
space:
mode:
Diffstat (limited to 'libcxx/test/std')
-rw-r--r--libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique.pass.cpp208
-rw-r--r--libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique_copy.pass.cpp402
-rw-r--r--libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/unique_copy.pass.cpp23
-rw-r--r--libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp2
-rw-r--r--libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp8
-rw-r--r--libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp4
-rw-r--r--libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp4
-rw-r--r--libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp4
-rw-r--r--libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp4
9 files changed, 638 insertions, 21 deletions
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique.pass.cpp
index 216ae6a1b98e..bb60109e7735 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique.pass.cpp
@@ -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;
}
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique_copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique_copy.pass.cpp
index 8ccff619b811..ac7965c8eb2c 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique_copy.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique_copy.pass.cpp
@@ -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;
}
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/unique_copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/unique_copy.pass.cpp
index bda304cd7493..bcf9c99e24c2 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/unique_copy.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/unique_copy.pass.cpp
@@ -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
diff --git a/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp b/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp
index a694376ca6ec..875ca35b1265 100644
--- a/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp
@@ -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>>);
diff --git a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
index e147c875902c..b8625f5a3f44 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
@@ -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);
diff --git a/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp
index eeeec9c1f57f..0d1e3990b3b1 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp
@@ -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);
diff --git a/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp
index aa0b15db6dee..c6fa8598e613 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp
@@ -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);
diff --git a/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp
index 4013065eb42a..191b4953f025 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp
@@ -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())
diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
index 1fc6dfb89c4b..eac6eece9ac7 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
@@ -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]