1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9// UNSUPPORTED: c++03, c++11, c++14, c++17
10
11// constexpr auto begin();
12
13#include <array>
14#include <cassert>
15#include <ranges>
16#include <type_traits>
17#include <utility>
18
19#include "test_iterators.h"
20
21struct View : std::ranges::view_base {
22 int* begin() const;
23 int* end() const;
24};
25
26// Test that begin is not const
27template <class T>
28concept HasBegin = requires(T t) { t.begin(); };
29
30struct Pred {
31 constexpr bool operator()(int i) const { return i < 3; }
32};
33
34static_assert(HasBegin<std::ranges::drop_while_view<View, Pred>>);
35static_assert(!HasBegin<const std::ranges::drop_while_view<View, Pred>>);
36
37constexpr auto always = [](auto v) { return [v](auto&&...) { return v; }; };
38
39template <class Iter>
40constexpr void testOne() {
41 using Sent = sentinel_wrapper<Iter>;
42 using Range = std::ranges::subrange<Iter, Sent>;
43 constexpr auto make_subrange = []<std::size_t N>(int(&buffer)[N]) {
44 return Range{Iter{buffer}, Sent{Iter{buffer + N}}};
45 };
46
47 // empty
48 {
49 std::array<int, 0> a;
50 Range range{Iter{a.data()}, Sent{Iter{a.data() + a.size()}}};
51 std::ranges::drop_while_view dwv{std::move(range), always(false)};
52 std::same_as<Iter> decltype(auto) it = dwv.begin();
53 assert(base(it) == a.data() + a.size());
54 }
55
56 // 1 element not dropped
57 {
58 int buffer[] = {1};
59 auto range = make_subrange(buffer);
60 std::ranges::drop_while_view dwv{std::move(range), always(false)};
61 std::same_as<Iter> decltype(auto) it = dwv.begin();
62 assert(base(it) == buffer);
63 }
64
65 // 1 element dropped
66 {
67 int buffer[] = {1};
68 auto range = make_subrange(buffer);
69 std::ranges::drop_while_view dwv{std::move(range), always(true)};
70 std::same_as<Iter> decltype(auto) it = dwv.begin();
71 assert(base(it) == buffer + 1);
72 }
73
74 // multiple elements. no element dropped
75 {
76 int buffer[] = {1, 2, 3, 4, 5};
77 auto range = make_subrange(buffer);
78 std::ranges::drop_while_view dwv{std::move(range), always(false)};
79 std::same_as<Iter> decltype(auto) it = dwv.begin();
80 assert(base(it) == buffer);
81 }
82
83 // multiple elements. all elements dropped
84 {
85 int buffer[] = {1, 2, 3, 4, 5};
86 auto range = make_subrange(buffer);
87 std::ranges::drop_while_view dwv{std::move(range), always(true)};
88 std::same_as<Iter> decltype(auto) it = dwv.begin();
89 assert(base(it) == buffer + 5);
90 }
91
92 // multiple elements. some elements dropped
93 {
94 int buffer[] = {1, 2, 3, 2, 1};
95 auto range = make_subrange(buffer);
96 std::ranges::drop_while_view dwv{std::move(range), [](int i) { return i < 3; }};
97 std::same_as<Iter> decltype(auto) it = dwv.begin();
98 assert(base(it) == buffer + 2);
99 }
100
101 // Make sure we do not make a copy of the predicate when we call begin()
102 {
103 struct TrackingPred {
104 constexpr explicit TrackingPred(bool* moved, bool* copied) : moved_(moved), copied_(copied) {}
105 constexpr TrackingPred(TrackingPred const& other) : moved_(other.moved_), copied_(other.copied_) {
106 *copied_ = true;
107 }
108 constexpr TrackingPred(TrackingPred&& other) : moved_(other.moved_), copied_(other.copied_) { *moved_ = true; }
109 TrackingPred& operator=(TrackingPred const&) = default;
110 TrackingPred& operator=(TrackingPred&&) = default;
111
112 constexpr bool operator()(int i) const { return i < 3; }
113 bool* moved_;
114 bool* copied_;
115 };
116
117 int buffer[] = {1, 2, 3, 2, 1};
118 bool moved = false, copied = false;
119 auto range = make_subrange(buffer);
120 std::ranges::drop_while_view dwv{std::move(range), TrackingPred(&moved, &copied)};
121 moved = false;
122 copied = false;
123 [[maybe_unused]] auto it = dwv.begin();
124 assert(!moved);
125 assert(!copied);
126 }
127
128 // Test with a non-const predicate
129 {
130 int buffer[] = {1, 2, 3, 2, 1};
131 auto range = make_subrange(buffer);
132 std::ranges::drop_while_view dwv{std::move(range), [](int i) mutable { return i < 3; }};
133 std::same_as<Iter> decltype(auto) it = dwv.begin();
134 assert(base(it) == buffer + 2);
135 }
136
137 // Test with a predicate that takes by non-const reference
138 {
139 int buffer[] = {1, 2, 3, 2, 1};
140 auto range = make_subrange(buffer);
141 std::ranges::drop_while_view dwv{std::move(range), [](int& i) { return i < 3; }};
142 std::same_as<Iter> decltype(auto) it = dwv.begin();
143 assert(base(it) == buffer + 2);
144 }
145
146 if constexpr (std::forward_iterator<Iter>) {
147 // Make sure that we cache the result of begin() on subsequent calls
148 {
149 int buffer[] = {1, 2, 3, 2, 1};
150 auto range = make_subrange(buffer);
151
152 int called = 0;
153 auto pred = [&](int i) {
154 ++called;
155 return i < 3;
156 };
157 std::ranges::drop_while_view dwv{range, pred};
158 for (auto i = 0; i < 10; ++i) {
159 std::same_as<Iter> decltype(auto) it = dwv.begin();
160 assert(base(it) == buffer + 2);
161 assert(called == 3);
162 }
163 }
164 }
165}
166
167constexpr bool test() {
168 testOne<cpp17_input_iterator<int*>>();
169 testOne<cpp20_input_iterator<int*>>();
170 testOne<forward_iterator<int*>>();
171 testOne<bidirectional_iterator<int*>>();
172 testOne<random_access_iterator<int*>>();
173 testOne<contiguous_iterator<int*>>();
174 testOne<int*>();
175 return true;
176}
177
178int main(int, char**) {
179 test();
180 static_assert(test());
181 return 0;
182}
183

source code of libcxx/test/std/ranges/range.adaptors/range.drop.while/begin.pass.cpp