//===----------------------------------------------------------------------===// // // 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 // ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-sign-compare // MSVC warning C4242: 'argument': conversion from 'const _Ty' to 'ElementT', possible loss of data // MSVC warning C4244: 'argument': conversion from 'const _Ty' to 'ElementT', possible loss of data // ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4242 /wd4244 // template S, class T, class Proj = identity> // requires indirect_binary_predicate, const T*> // constexpr I ranges::find(I first, S last, const T& value, Proj proj = {}); // template // requires indirect_binary_predicate, Proj>, const T*> // constexpr borrowed_iterator_t // ranges::find(R&& r, const T& value, Proj proj = {}); #include #include #include #include #include #include #include "almost_satisfies_types.h" #include "test_iterators.h" struct NotEqualityComparable {}; template concept HasFindIt = requires(It it, Sent sent) { std::ranges::find(it, sent, *it); }; static_assert(HasFindIt); static_assert(!HasFindIt); static_assert(!HasFindIt); static_assert(!HasFindIt); static_assert(!HasFindIt); static_assert(!HasFindIt, SentinelForNotSemiregular>); static_assert(!HasFindIt, InputRangeNotSentinelEqualityComparableWith>); static_assert(!HasFindIt); static_assert(!HasFindIt); template concept HasFindR = requires(Range r) { std::ranges::find(r, ValT{}); }; static_assert(HasFindR, int>); static_assert(!HasFindR); static_assert(!HasFindR, NotEqualityComparable>); static_assert(!HasFindR); static_assert(!HasFindR); static_assert(!HasFindR); static_assert(!HasFindR); static_assert(!HasFindR); static std::vector comparable_data; template constexpr void test_iterators() { using ValueT = std::iter_value_t; { // simple test { ValueT a[] = {1, 2, 3, 4}; std::same_as auto ret = std::ranges::find(It(a), Sent(It(a + 4)), 4); assert(base(ret) == a + 3); assert(*ret == 4); } { ValueT a[] = {1, 2, 3, 4}; auto range = std::ranges::subrange(It(a), Sent(It(a + 4))); std::same_as auto ret = std::ranges::find(range, 4); assert(base(ret) == a + 3); assert(*ret == 4); } } { // check that an empty range works { std::array a = {}; auto ret = std::ranges::find(It(a.data()), Sent(It(a.data())), 1); assert(base(ret) == a.data()); } { std::array a = {}; auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data()))); auto ret = std::ranges::find(range, 1); assert(base(ret) == a.data()); } } { // check that last is returned with no match { ValueT a[] = {1, 1, 1}; auto ret = std::ranges::find(a, a + 3, 0); assert(ret == a + 3); } { ValueT a[] = {1, 1, 1}; auto ret = std::ranges::find(a, 0); assert(ret == a + 3); } } if (!std::is_constant_evaluated()) comparable_data.clear(); } template class TriviallyComparable { ElementT el_; public: TEST_CONSTEXPR TriviallyComparable(ElementT el) : el_(el) {} bool operator==(const TriviallyComparable&) const = default; }; constexpr bool test() { types::for_each(types::type_list, TriviallyComparable>{}, [] { types::for_each(types::cpp20_input_iterator_list{}, [] { if constexpr (std::forward_iterator) test_iterators(); test_iterators>(); test_iterators>(); }); }); // TODO: Remove the `_LIBCPP_ENABLE_EXPERIMENTAL` check once we have the FTM guarded or views::join isn't // experimental anymore #if TEST_STD_VER >= 20 && (!defined(_LIBCPP_VERSION) || defined(_LIBCPP_ENABLE_EXPERIMENTAL)) { std::vector> vec = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; auto view = vec | std::views::join; assert(std::ranges::find(view.begin(), view.end(), 4) == std::next(view.begin(), 3)); assert(std::ranges::find(view, 4) == std::next(view.begin(), 3)); } #endif { // check that the first element is returned { struct S { int comp; int other; }; S a[] = { {0, 0}, {0, 2}, {0, 1} }; auto ret = std::ranges::find(a, 0, &S::comp); assert(ret == a); assert(ret->comp == 0); assert(ret->other == 0); } { struct S { int comp; int other; }; S a[] = { {0, 0}, {0, 2}, {0, 1} }; auto ret = std::ranges::find(a, a + 3, 0, &S::comp); assert(ret == a); assert(ret->comp == 0); assert(ret->other == 0); } } { // check that an iterator is returned with a borrowing range int a[] = {1, 2, 3, 4}; std::same_as auto ret = std::ranges::find(std::views::all(a), 1); assert(ret == a); assert(*ret == 1); } { // count invocations of the projection { int a[] = {1, 2, 3, 4}; int projection_count = 0; auto ret = std::ranges::find(a, a + 4, 2, [&](int i) { ++projection_count; return i; }); assert(ret == a + 1); assert(*ret == 2); assert(projection_count == 2); } { int a[] = {1, 2, 3, 4}; int projection_count = 0; auto ret = std::ranges::find(a, 2, [&](int i) { ++projection_count; return i; }); assert(ret == a + 1); assert(*ret == 2); assert(projection_count == 2); } } return true; } template class Comparable { IndexT index_; public: Comparable(IndexT i) : index_([&]() { IndexT size = static_cast(comparable_data.size()); comparable_data.push_back(i); return size; }()) {} bool operator==(const Comparable& other) const { return comparable_data[other.index_] == comparable_data[index_]; } friend bool operator==(const Comparable& lhs, long long rhs) { return comparable_data[lhs.index_] == rhs; } }; void test_deque() { { // empty deque std::deque data; assert(std::ranges::find(data, 4) == data.end()); assert(std::ranges::find(data.begin(), data.end(), 4) == data.end()); } { // single element - match std::deque data = {4}; assert(std::ranges::find(data, 4) == data.begin()); assert(std::ranges::find(data.begin(), data.end(), 4) == data.begin()); } { // single element - no match std::deque data = {3}; assert(std::ranges::find(data, 4) == data.end()); assert(std::ranges::find(data.begin(), data.end(), 4) == data.end()); } // many elements for (auto size : {2, 3, 1023, 1024, 1025, 2047, 2048, 2049}) { { // last element match std::deque data; data.resize(size); std::fill(data.begin(), data.end(), 3); data[size - 1] = 4; assert(std::ranges::find(data, 4) == data.end() - 1); assert(std::ranges::find(data.begin(), data.end(), 4) == data.end() - 1); } { // second-last element match std::deque data; data.resize(size); std::fill(data.begin(), data.end(), 3); data[size - 2] = 4; assert(std::ranges::find(data, 4) == data.end() - 2); assert(std::ranges::find(data.begin(), data.end(), 4) == data.end() - 2); } { // no match std::deque data; data.resize(size); std::fill(data.begin(), data.end(), 3); assert(std::ranges::find(data, 4) == data.end()); assert(std::ranges::find(data.begin(), data.end(), 4) == data.end()); } } } int main(int, char**) { test_deque(); test(); static_assert(test()); types::for_each(types::cpp20_input_iterator_list*>{}, [] { if constexpr (std::forward_iterator) test_iterators(); test_iterators>(); test_iterators>(); }); types::for_each(types::cpp20_input_iterator_list*>{}, [] { if constexpr (std::forward_iterator) test_iterators(); test_iterators>(); test_iterators>(); }); return 0; }