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// std::ranges::size
12
13#include <ranges>
14
15#include <cassert>
16#include "test_macros.h"
17#include "test_iterators.h"
18
19using RangeSizeT = decltype(std::ranges::size);
20
21static_assert(!std::is_invocable_v<RangeSizeT, int[]>);
22static_assert( std::is_invocable_v<RangeSizeT, int[1]>);
23static_assert( std::is_invocable_v<RangeSizeT, int (&&)[1]>);
24static_assert( std::is_invocable_v<RangeSizeT, int (&)[1]>);
25
26struct Incomplete;
27static_assert(!std::is_invocable_v<RangeSizeT, Incomplete[]>);
28static_assert(!std::is_invocable_v<RangeSizeT, Incomplete(&)[]>);
29static_assert(!std::is_invocable_v<RangeSizeT, Incomplete(&&)[]>);
30
31extern Incomplete array_of_incomplete[42];
32static_assert(std::ranges::size(array_of_incomplete) == 42);
33static_assert(std::ranges::size(std::move(array_of_incomplete)) == 42);
34static_assert(std::ranges::size(std::as_const(array_of_incomplete)) == 42);
35static_assert(std::ranges::size(static_cast<const Incomplete(&&)[42]>(array_of_incomplete)) == 42);
36
37struct SizeMember {
38 constexpr std::size_t size() { return 42; }
39};
40
41struct StaticSizeMember {
42 constexpr static std::size_t size() { return 42; }
43};
44
45static_assert(!std::is_invocable_v<RangeSizeT, const SizeMember>);
46
47struct SizeFunction {
48 friend constexpr std::size_t size(SizeFunction) { return 42; }
49};
50
51// Make sure the size member is preferred.
52struct SizeMemberAndFunction {
53 constexpr std::size_t size() { return 42; }
54 friend constexpr std::size_t size(SizeMemberAndFunction) { return 0; }
55};
56
57bool constexpr testArrayType() {
58 int a[4];
59 int b[1];
60 SizeMember c[4];
61 SizeFunction d[4];
62
63 assert(std::ranges::size(a) == 4);
64 ASSERT_SAME_TYPE(decltype(std::ranges::size(a)), std::size_t);
65 assert(std::ranges::size(b) == 1);
66 ASSERT_SAME_TYPE(decltype(std::ranges::size(b)), std::size_t);
67 assert(std::ranges::size(c) == 4);
68 ASSERT_SAME_TYPE(decltype(std::ranges::size(c)), std::size_t);
69 assert(std::ranges::size(d) == 4);
70 ASSERT_SAME_TYPE(decltype(std::ranges::size(d)), std::size_t);
71
72 return true;
73}
74
75struct SizeMemberConst {
76 constexpr std::size_t size() const { return 42; }
77};
78
79struct SizeMemberSigned {
80 constexpr long size() { return 42; }
81};
82
83bool constexpr testHasSizeMember() {
84 assert(std::ranges::size(SizeMember()) == 42);
85 ASSERT_SAME_TYPE(decltype(std::ranges::size(SizeMember())), std::size_t);
86
87 const SizeMemberConst sizeMemberConst;
88 assert(std::ranges::size(sizeMemberConst) == 42);
89
90 assert(std::ranges::size(SizeMemberAndFunction()) == 42);
91
92 assert(std::ranges::size(SizeMemberSigned()) == 42);
93 ASSERT_SAME_TYPE(decltype(std::ranges::size(SizeMemberSigned())), long);
94
95 assert(std::ranges::size(StaticSizeMember()) == 42);
96 ASSERT_SAME_TYPE(decltype(std::ranges::size(StaticSizeMember())), std::size_t);
97
98 return true;
99}
100
101struct MoveOnlySizeFunction {
102 MoveOnlySizeFunction() = default;
103 MoveOnlySizeFunction(MoveOnlySizeFunction &&) = default;
104 MoveOnlySizeFunction(MoveOnlySizeFunction const&) = delete;
105
106 friend constexpr std::size_t size(MoveOnlySizeFunction) { return 42; }
107};
108
109enum EnumSizeFunction {
110 a, b
111};
112
113constexpr std::size_t size(EnumSizeFunction) { return 42; }
114
115struct SizeFunctionConst {
116 friend constexpr std::size_t size(const SizeFunctionConst) { return 42; }
117};
118
119struct SizeFunctionRef {
120 friend constexpr std::size_t size(SizeFunctionRef&) { return 42; }
121};
122
123struct SizeFunctionConstRef {
124 friend constexpr std::size_t size(SizeFunctionConstRef const&) { return 42; }
125};
126
127struct SizeFunctionSigned {
128 friend constexpr long size(SizeFunctionSigned) { return 42; }
129};
130
131bool constexpr testHasSizeFunction() {
132 assert(std::ranges::size(SizeFunction()) == 42);
133 ASSERT_SAME_TYPE(decltype(std::ranges::size(SizeFunction())), std::size_t);
134 static_assert(!std::is_invocable_v<RangeSizeT, MoveOnlySizeFunction>);
135 assert(std::ranges::size(EnumSizeFunction()) == 42);
136 assert(std::ranges::size(SizeFunctionConst()) == 42);
137
138 SizeFunctionRef a;
139 assert(std::ranges::size(a) == 42);
140
141 const SizeFunctionConstRef b;
142 assert(std::ranges::size(b) == 42);
143
144 assert(std::ranges::size(SizeFunctionSigned()) == 42);
145 ASSERT_SAME_TYPE(decltype(std::ranges::size(SizeFunctionSigned())), long);
146
147 return true;
148}
149
150struct Empty { };
151static_assert(!std::is_invocable_v<RangeSizeT, Empty>);
152
153struct InvalidReturnTypeMember {
154 Empty size();
155};
156
157struct InvalidReturnTypeFunction {
158 friend Empty size(InvalidReturnTypeFunction);
159};
160
161struct Convertible {
162 operator std::size_t();
163};
164
165struct ConvertibleReturnTypeMember {
166 Convertible size();
167};
168
169struct ConvertibleReturnTypeFunction {
170 friend Convertible size(ConvertibleReturnTypeFunction);
171};
172
173struct BoolReturnTypeMember {
174 bool size() const;
175};
176
177struct BoolReturnTypeFunction {
178 friend bool size(BoolReturnTypeFunction const&);
179};
180
181static_assert(!std::is_invocable_v<RangeSizeT, InvalidReturnTypeMember>);
182static_assert(!std::is_invocable_v<RangeSizeT, InvalidReturnTypeFunction>);
183static_assert( std::is_invocable_v<RangeSizeT, InvalidReturnTypeMember (&)[4]>);
184static_assert( std::is_invocable_v<RangeSizeT, InvalidReturnTypeFunction (&)[4]>);
185static_assert(!std::is_invocable_v<RangeSizeT, ConvertibleReturnTypeMember>);
186static_assert(!std::is_invocable_v<RangeSizeT, ConvertibleReturnTypeFunction>);
187static_assert(!std::is_invocable_v<RangeSizeT, BoolReturnTypeMember const&>);
188static_assert(!std::is_invocable_v<RangeSizeT, BoolReturnTypeFunction const&>);
189
190struct SizeMemberDisabled {
191 std::size_t size() { return 42; }
192};
193
194template <>
195inline constexpr bool std::ranges::disable_sized_range<SizeMemberDisabled> = true;
196
197struct ImproperlyDisabledMember {
198 std::size_t size() const { return 42; }
199};
200
201// Intentionally disabling "const ConstSizeMemberDisabled". This doesn't disable anything
202// because T is always uncvrefed before being checked.
203template <>
204inline constexpr bool std::ranges::disable_sized_range<const ImproperlyDisabledMember> = true;
205
206struct SizeFunctionDisabled {
207 friend std::size_t size(SizeFunctionDisabled) { return 42; }
208};
209
210template <>
211inline constexpr bool std::ranges::disable_sized_range<SizeFunctionDisabled> = true;
212
213struct ImproperlyDisabledFunction {
214 friend std::size_t size(ImproperlyDisabledFunction const&) { return 42; }
215};
216
217template <>
218inline constexpr bool std::ranges::disable_sized_range<const ImproperlyDisabledFunction> = true;
219
220static_assert( std::is_invocable_v<RangeSizeT, ImproperlyDisabledMember&>);
221static_assert( std::is_invocable_v<RangeSizeT, const ImproperlyDisabledMember&>);
222static_assert(std::is_invocable_v<RangeSizeT,
223 ImproperlyDisabledFunction&>); // Ill-formed before P2602R2 Poison Pills are Too Toxic
224static_assert( std::is_invocable_v<RangeSizeT, const ImproperlyDisabledFunction&>);
225
226// No begin end.
227struct HasMinusOperator {
228 friend constexpr std::size_t operator-(HasMinusOperator, HasMinusOperator) { return 2; }
229};
230static_assert(!std::is_invocable_v<RangeSizeT, HasMinusOperator>);
231
232struct HasMinusBeginEnd {
233 struct sentinel {
234 friend bool operator==(sentinel, forward_iterator<int*>);
235 friend constexpr std::ptrdiff_t operator-(const sentinel, const forward_iterator<int*>) { return 2; }
236 friend constexpr std::ptrdiff_t operator-(const forward_iterator<int*>, const sentinel) { return 2; }
237 };
238
239 friend constexpr forward_iterator<int*> begin(HasMinusBeginEnd) { return {}; }
240 friend constexpr sentinel end(HasMinusBeginEnd) { return {}; }
241};
242
243struct other_forward_iterator : forward_iterator<int*> { };
244
245struct InvalidMinusBeginEnd {
246 struct sentinel {
247 friend bool operator==(sentinel, other_forward_iterator);
248 friend constexpr std::ptrdiff_t operator-(const sentinel, const other_forward_iterator) { return 2; }
249 friend constexpr std::ptrdiff_t operator-(const other_forward_iterator, const sentinel) { return 2; }
250 };
251
252 friend constexpr other_forward_iterator begin(InvalidMinusBeginEnd) { return {}; }
253 friend constexpr sentinel end(InvalidMinusBeginEnd) { return {}; }
254};
255
256// short is integer-like, but it is not other_forward_iterator's difference_type.
257static_assert(!std::same_as<other_forward_iterator::difference_type, short>);
258static_assert(!std::is_invocable_v<RangeSizeT, InvalidMinusBeginEnd>);
259
260struct RandomAccessRange {
261 struct sentinel {
262 friend bool operator==(sentinel, random_access_iterator<int*>);
263 friend constexpr std::ptrdiff_t operator-(const sentinel, const random_access_iterator<int*>) { return 2; }
264 friend constexpr std::ptrdiff_t operator-(const random_access_iterator<int*>, const sentinel) { return 2; }
265 };
266
267 constexpr random_access_iterator<int*> begin() { return {}; }
268 constexpr sentinel end() { return {}; }
269};
270
271struct IntPtrBeginAndEnd {
272 int buff[8];
273 constexpr int* begin() { return buff; }
274 constexpr int* end() { return buff + 8; }
275};
276
277struct DisabledSizeRangeWithBeginEnd {
278 int buff[8];
279 constexpr int* begin() { return buff; }
280 constexpr int* end() { return buff + 8; }
281 constexpr std::size_t size() { return 1; }
282};
283
284template <>
285inline constexpr bool std::ranges::disable_sized_range<DisabledSizeRangeWithBeginEnd> = true;
286
287struct SizeBeginAndEndMembers {
288 int buff[8];
289 constexpr int* begin() { return buff; }
290 constexpr int* end() { return buff + 8; }
291 constexpr std::size_t size() { return 1; }
292};
293
294constexpr bool testRanges() {
295 HasMinusBeginEnd a;
296 assert(std::ranges::size(a) == 2);
297 // Ensure that this is converted to an *unsigned* type.
298 ASSERT_SAME_TYPE(decltype(std::ranges::size(a)), std::size_t);
299
300 IntPtrBeginAndEnd b;
301 assert(std::ranges::size(b) == 8);
302
303 DisabledSizeRangeWithBeginEnd c;
304 assert(std::ranges::size(c) == 8);
305
306 RandomAccessRange d;
307 assert(std::ranges::size(d) == 2);
308 ASSERT_SAME_TYPE(decltype(std::ranges::size(d)), std::size_t);
309
310 SizeBeginAndEndMembers e;
311 assert(std::ranges::size(e) == 1);
312
313 return true;
314}
315
316// Test ADL-proofing.
317struct Incomplete;
318template<class T> struct Holder { T t; };
319static_assert(!std::is_invocable_v<RangeSizeT, Holder<Incomplete>*>);
320static_assert(!std::is_invocable_v<RangeSizeT, Holder<Incomplete>*&>);
321
322int main(int, char**) {
323 testArrayType();
324 static_assert(testArrayType());
325
326 testHasSizeMember();
327 static_assert(testHasSizeMember());
328
329 testHasSizeFunction();
330 static_assert(testHasSizeFunction());
331
332 testRanges();
333 static_assert(testRanges());
334
335 return 0;
336}
337

source code of libcxx/test/std/ranges/range.access/size.pass.cpp