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 // UNSUPPORTED: libcpp-has-no-incomplete-ranges
11 
12 // <algorithm>
13 
14 // template<input_iterator I, sentinel_for<I> S, weakly_incrementable O,
15 //          copy_constructible F, class Proj = identity>
16 //   requires indirectly_writable<O, indirect_result_t<F&, projected<I, Proj>>>
17 //   constexpr ranges::unary_transform_result<I, O>
18 //     ranges::transform(I first1, S last1, O result, F op, Proj proj = {});
19 // template<input_range R, weakly_incrementable O, copy_constructible F,
20 //          class Proj = identity>
21 //   requires indirectly_writable<O, indirect_result_t<F&, projected<iterator_t<R>, Proj>>>
22 //   constexpr ranges::unary_transform_result<borrowed_iterator_t<R>, O>
23 //     ranges::transform(R&& r, O result, F op, Proj proj = {});
24 // template<input_iterator I1, sentinel_for<I1> S1, input_iterator I2, sentinel_for<I2> S2,
25 //          weakly_incrementable O, copy_constructible F, class Proj1 = identity,
26 //          class Proj2 = identity>
27 //   requires indirectly_writable<O, indirect_result_t<F&, projected<I1, Proj1>,
28 //                                          projected<I2, Proj2>>>
29 //   constexpr ranges::binary_transform_result<I1, I2, O>
30 //     ranges::transform(I1 first1, S1 last1, I2 first2, S2 last2, O result,
31 //                       F binary_op, Proj1 proj1 = {}, Proj2 proj2 = {});
32 // template<input_range R1, input_range R2, weakly_incrementable O,
33 //          copy_constructible F, class Proj1 = identity, class Proj2 = identity>
34 //   requires indirectly_writable<O, indirect_result_t<F&, projected<iterator_t<R1>, Proj1>,
35 //                                          projected<iterator_t<R2>, Proj2>>>
36 //   constexpr ranges::binary_transform_result<borrowed_iterator_t<R1>, borrowed_iterator_t<R2>, O>
37 //     ranges::transform(R1&& r1, R2&& r2, O result,
38 //                       F binary_op, Proj1 proj1 = {}, Proj2 proj2 = {});
39 
40 #include <algorithm>
41 #include <array>
42 #include <cassert>
43 #include <functional>
44 #include <ranges>
45 
46 #include "test_iterators.h"
47 #include "almost_satisfies_types.h"
48 
49 struct BinaryFunc {
50   int operator()(int, int);
51 };
52 
53 template <class Range>
54 concept HasTranformR = requires(Range r, int* out) {
55   std::ranges::transform(r, out, std::identity{});
56   std::ranges::transform(r, r, out, BinaryFunc{});
57 };
58 static_assert(HasTranformR<std::array<int, 1>>);
59 static_assert(!HasTranformR<int>);
60 static_assert(!HasTranformR<InputRangeNotDerivedFrom>);
61 static_assert(!HasTranformR<InputRangeNotIndirectlyReadable>);
62 static_assert(!HasTranformR<InputRangeNotInputOrOutputIterator>);
63 static_assert(!HasTranformR<InputRangeNotSentinelSemiregular>);
64 static_assert(!HasTranformR<InputRangeNotSentinelEqualityComparableWith>);
65 
66 template <class It, class Sent = It>
67 concept HasTransformIt = requires(It it, Sent sent, int* out) {
68   std::ranges::transform(it, sent, out, std::identity{});
69   std::ranges::transform(it, sent, it, sent, out, BinaryFunc{});
70 };
71 static_assert(HasTransformIt<int*>);
72 static_assert(!HasTransformIt<InputIteratorNotDerivedFrom>);
73 static_assert(!HasTransformIt<InputIteratorNotIndirectlyReadable>);
74 static_assert(!HasTransformIt<InputIteratorNotInputOrOutputIterator>);
75 static_assert(!HasTransformIt<cpp20_input_iterator<int*>, SentinelForNotSemiregular>);
76 static_assert(!HasTransformIt<cpp20_input_iterator<int*>, InputRangeNotSentinelEqualityComparableWith>);
77 
78 template <class It>
79 concept HasTransformOut = requires(int* it, int* sent, It out, std::array<int, 2> range) {
80   std::ranges::transform(it, sent, out, std::identity{});
81   std::ranges::transform(it, sent, it, sent, out, BinaryFunc{});
82   std::ranges::transform(range, out, std::identity{});
83   std::ranges::transform(range, range, out, BinaryFunc{});
84 };
85 static_assert(HasTransformOut<int*>);
86 static_assert(!HasTransformOut<WeaklyIncrementableNotMovable>);
87 
88 // check indirectly_readable
89 static_assert(HasTransformOut<char*>);
90 static_assert(!HasTransformOut<int**>);
91 
92 struct MoveOnlyFunctor {
93   MoveOnlyFunctor(const MoveOnlyFunctor&) = delete;
94   MoveOnlyFunctor(MoveOnlyFunctor&&) = default;
95   int operator()(int);
96   int operator()(int, int);
97 };
98 
99 template <class Func>
100 concept HasTransformFuncUnary = requires(int* it, int* sent, int* out, std::array<int, 2> range, Func func) {
101   std::ranges::transform(it, sent, out, func);
102   std::ranges::transform(range, out, func);
103 };
104 static_assert(HasTransformFuncUnary<std::identity>);
105 static_assert(!HasTransformFuncUnary<MoveOnlyFunctor>);
106 
107 template <class Func>
108 concept HasTransformFuncBinary = requires(int* it, int* sent, int* out, std::array<int, 2> range, Func func) {
109   std::ranges::transform(it, sent, it, sent, out, func);
110   std::ranges::transform(range, range, out, func);
111 };
112 static_assert(HasTransformFuncBinary<BinaryFunc>);
113 static_assert(!HasTransformFuncBinary<MoveOnlyFunctor>);
114 
115 static_assert(std::is_same_v<std::ranges::unary_transform_result<int, long>, std::ranges::in_out_result<int, long>>);
116 static_assert(std::is_same_v<std::ranges::binary_transform_result<int, long, char>,
117                              std::ranges::in_in_out_result<int, long, char>>);
118 
119 template <class In1, class In2, class Out, class Sent1, class Sent2>
test_iterators()120 constexpr bool test_iterators() {
121   { // simple
122     { // unary
123       {
124         int a[] = {1, 2, 3, 4, 5};
125         int b[5];
126         std::same_as<std::ranges::in_out_result<In1, Out>> decltype(auto) ret =
127           std::ranges::transform(In1(a), Sent1(In1(a + 5)), Out(b), [](int i) { return i * 2; });
128         assert((std::to_array(b) == std::array{2, 4, 6, 8, 10}));
129         assert(base(ret.in) == a + 5);
130         assert(base(ret.out) == b + 5);
131       }
132 
133       {
134         int a[] = {1, 2, 3, 4, 5};
135         int b[5];
136         auto range = std::ranges::subrange(In1(a), Sent1(In1(a + 5)));
137         std::same_as<std::ranges::in_out_result<In1, Out>> decltype(auto) ret =
138           std::ranges::transform(range, Out(b), [](int i) { return i * 2; });
139         assert((std::to_array(b) == std::array{2, 4, 6, 8, 10}));
140         assert(base(ret.in) == a + 5);
141         assert(base(ret.out) == b + 5);
142       }
143     }
144 
145     { // binary
146       {
147         int a[] = {1, 2, 3, 4, 5};
148         int b[] = {5, 4, 3, 2, 1};
149         int c[5];
150 
151         std::same_as<std::ranges::in_in_out_result<In1, In2, Out>> decltype(auto) ret = std::ranges::transform(
152             In1(a), Sent1(In1(a + 5)), In2(b), Sent2(In2(b + 5)), Out(c), [](int i, int j) { return i + j; });
153 
154         assert((std::to_array(c) == std::array{6, 6, 6, 6, 6}));
155         assert(base(ret.in1) == a + 5);
156         assert(base(ret.in2) == b + 5);
157         assert(base(ret.out) == c + 5);
158       }
159 
160       {
161         int a[] = {1, 2, 3, 4, 5};
162         int b[] = {5, 4, 3, 2, 1};
163         int c[5];
164 
165         auto range1 = std::ranges::subrange(In1(a), Sent1(In1(a + 5)));
166         auto range2 = std::ranges::subrange(In2(b), Sent2(In2(b + 5)));
167 
168         std::same_as<std::ranges::in_in_out_result<In1, In2, Out>> decltype(auto) ret = std::ranges::transform(
169             range1, range2, Out(c), [](int i, int j) { return i + j; });
170 
171         assert((std::to_array(c) == std::array{6, 6, 6, 6, 6}));
172         assert(base(ret.in1) == a + 5);
173         assert(base(ret.in2) == b + 5);
174         assert(base(ret.out) == c + 5);
175       }
176     }
177   }
178 
179   { // first range empty
180     { // unary
181       {
182         int a[] = {};
183         int b[5];
184         auto ret = std::ranges::transform(In1(a), Sent1(In1(a)), Out(b), [](int i) { return i * 2; });
185         assert(base(ret.in) == a);
186         assert(base(ret.out) == b);
187       }
188 
189       {
190         int a[] = {};
191         int b[5];
192         auto range = std::ranges::subrange(In1(a), Sent1(In1(a)));
193         auto ret = std::ranges::transform(range, Out(b), [](int i) { return i * 2; });
194         assert(base(ret.in) == a);
195         assert(base(ret.out) == b);
196       }
197     }
198 
199     { // binary
200       {
201         int a[] = {};
202         int b[] = {5, 4, 3, 2, 1};
203         int c[5];
204 
205         auto ret = std::ranges::transform(
206             In1(a), Sent1(In1(a)), In2(b), Sent2(In2(b + 5)), Out(c), [](int i, int j) { return i + j; });
207 
208         assert(base(ret.in1) == a);
209         assert(base(ret.in2) == b);
210         assert(base(ret.out) == c);
211       }
212 
213       {
214         int a[] = {};
215         int b[] = {5, 4, 3, 2, 1};
216         int c[5];
217 
218         auto range1 = std::ranges::subrange(In1(a), Sent1(In1(a)));
219         auto range2 = std::ranges::subrange(In2(b), Sent2(In2(b + 5)));
220 
221         auto ret = std::ranges::transform(range1, range2, Out(c), [](int i, int j) { return i + j; });
222 
223         assert(base(ret.in1) == a);
224         assert(base(ret.in2) == b);
225         assert(base(ret.out) == c);
226       }
227     }
228   }
229 
230   { // second range empty (binary)
231     {
232       int a[] = {5, 4, 3, 2, 1};
233       int b[] = {};
234       int c[5];
235 
236       auto ret = std::ranges::transform(
237           In1(a), Sent1(In1(a + 5)), In2(b), Sent2(In2(b)), Out(c), [](int i, int j) { return i + j; });
238 
239       assert(base(ret.in1) == a);
240       assert(base(ret.in2) == b);
241       assert(base(ret.out) == c);
242     }
243 
244     {
245       int a[] = {5, 4, 3, 2, 1};
246       int b[] = {};
247       int c[5];
248 
249       auto range1 = std::ranges::subrange(In1(a), Sent1(In1(a + 5)));
250       auto range2 = std::ranges::subrange(In2(b), Sent2(In2(b)));
251 
252       auto ret = std::ranges::transform(range1, range2, Out(c), [](int i, int j) { return i + j; });
253 
254       assert(base(ret.in1) == a);
255       assert(base(ret.in2) == b);
256       assert(base(ret.out) == c);
257     }
258   }
259 
260   { // both ranges empty (binary)
261     {
262       int a[] = {};
263       int b[] = {};
264       int c[5];
265 
266       auto ret = std::ranges::transform(
267           In1(a), Sent1(In1(a)), In2(b), Sent2(In2(b)), Out(c), [](int i, int j) { return i + j; });
268 
269       assert(base(ret.in1) == a);
270       assert(base(ret.in2) == b);
271       assert(base(ret.out) == c);
272     }
273 
274     {
275       int a[] = {};
276       int b[] = {};
277       int c[5];
278 
279       auto range1 = std::ranges::subrange(In1(a), Sent1(In1(a)));
280       auto range2 = std::ranges::subrange(In2(b), Sent2(In2(b)));
281 
282       auto ret = std::ranges::transform(range1, range2, Out(c), [](int i, int j) { return i + j; });
283 
284       assert(base(ret.in1) == a);
285       assert(base(ret.in2) == b);
286       assert(base(ret.out) == c);
287     }
288   }
289 
290   { // first range one element
291     { // unary
292       {
293         int a[] = {2};
294         int b[5];
295         auto ret = std::ranges::transform(In1(a), Sent1(In1(a + 1)), Out(b), [](int i) { return i * 2; });
296         assert(b[0] == 4);
297         assert(base(ret.in) == a + 1);
298         assert(base(ret.out) == b + 1);
299       }
300 
301       {
302         int a[] = {2};
303         int b[5];
304         auto range = std::ranges::subrange(In1(a), Sent1(In1(a + 1)));
305         auto ret = std::ranges::transform(range, Out(b), [](int i) { return i * 2; });
306         assert(b[0] == 4);
307         assert(base(ret.in) == a + 1);
308         assert(base(ret.out) == b + 1);
309       }
310     }
311 
312     { // binary
313       {
314         int a[] = {2};
315         int b[] = {5, 4, 3, 2, 1};
316         int c[5];
317 
318         auto ret = std::ranges::transform(
319             In1(a), Sent1(In1(a + 1)), In2(b), Sent2(In2(b + 5)), Out(c), [](int i, int j) { return i + j; });
320 
321         assert(c[0] == 7);
322         assert(base(ret.in1) == a + 1);
323         assert(base(ret.in2) == b + 1);
324         assert(base(ret.out) == c + 1);
325       }
326 
327       {
328         int a[] = {2};
329         int b[] = {5, 4, 3, 2, 1};
330         int c[5];
331 
332         auto range1 = std::ranges::subrange(In1(a), Sent1(In1(a + 1)));
333         auto range2 = std::ranges::subrange(In2(b), Sent2(In2(b + 5)));
334 
335         auto ret = std::ranges::transform(range1, range2, Out(c), [](int i, int j) { return i + j; });
336 
337         assert(c[0] == 7);
338         assert(base(ret.in1) == a + 1);
339         assert(base(ret.in2) == b + 1);
340         assert(base(ret.out) == c + 1);
341       }
342     }
343   }
344 
345   { // second range contains one element (binary)
346     {
347       int a[] = {5, 4, 3, 2, 1};
348       int b[] = {4};
349       int c[5];
350 
351       auto ret = std::ranges::transform(
352           In1(a), Sent1(In1(a + 5)), In2(b), Sent2(In2(b + 1)), Out(c), [](int i, int j) { return i + j; });
353 
354       assert(c[0] == 9);
355       assert(base(ret.in1) == a + 1);
356       assert(base(ret.in2) == b + 1);
357       assert(base(ret.out) == c + 1);
358     }
359 
360     {
361       int a[] = {5, 4, 3, 2, 1};
362       int b[] = {4};
363       int c[5];
364 
365       auto range1 = std::ranges::subrange(In1(a), Sent1(In1(a + 5)));
366       auto range2 = std::ranges::subrange(In2(b), Sent2(In2(b + 1)));
367 
368       auto ret = std::ranges::transform(range1, range2, Out(c), [](int i, int j) { return i + j; });
369 
370       assert(c[0] == 9);
371       assert(base(ret.in1) == a + 1);
372       assert(base(ret.in2) == b + 1);
373       assert(base(ret.out) == c + 1);
374     }
375   }
376 
377   { // check that the transform function and projection call counts are correct
378     { // unary
379       {
380         int predCount = 0;
381         int projCount = 0;
382         auto pred = [&](int) { ++predCount; return 1; };
383         auto proj = [&](int) { ++projCount; return 0; };
384         int a[] = {1, 2, 3, 4};
385         std::array<int, 4> c;
386         std::ranges::transform(In1(a), Sent1(In1(a + 4)), Out(c.data()), pred, proj);
387         assert(predCount == 4);
388         assert(projCount == 4);
389         assert((c == std::array{1, 1, 1, 1}));
390       }
391       {
392         int predCount = 0;
393         int projCount = 0;
394         auto pred = [&](int) { ++predCount; return 1; };
395         auto proj = [&](int) { ++projCount; return 0; };
396         int a[] = {1, 2, 3, 4};
397         std::array<int, 4> c;
398         auto range = std::ranges::subrange(In1(a), Sent1(In1(a + 4)));
399         std::ranges::transform(range, Out(c.data()), pred, proj);
400         assert(predCount == 4);
401         assert(projCount == 4);
402         assert((c == std::array{1, 1, 1, 1}));
403       }
404     }
405     { // binary
406       {
407         int predCount = 0;
408         int proj1Count = 0;
409         int proj2Count = 0;
410         auto pred = [&](int, int) { ++predCount; return 1; };
411         auto proj1 = [&](int) { ++proj1Count; return 0; };
412         auto proj2 = [&](int) { ++proj2Count; return 0; };
413         int a[] = {1, 2, 3, 4};
414         int b[] = {1, 2, 3, 4};
415         std::array<int, 4> c;
416         std::ranges::transform(In1(a), Sent1(In1(a + 4)), In2(b), Sent2(In2(b + 4)), Out(c.data()), pred, proj1, proj2);
417         assert(predCount == 4);
418         assert(proj1Count == 4);
419         assert(proj2Count == 4);
420         assert((c == std::array{1, 1, 1, 1}));
421       }
422       {
423         int predCount = 0;
424         int proj1Count = 0;
425         int proj2Count = 0;
426         auto pred = [&](int, int) { ++predCount; return 1; };
427         auto proj1 = [&](int) { ++proj1Count; return 0; };
428         auto proj2 = [&](int) { ++proj2Count; return 0; };
429         int a[] = {1, 2, 3, 4};
430         int b[] = {1, 2, 3, 4};
431         std::array<int, 4> c;
432         auto range1 = std::ranges::subrange(In1(a), Sent1(In1(a + 4)));
433         auto range2 = std::ranges::subrange(In2(b), Sent2(In2(b + 4)));
434         std::ranges::transform(range1, range2, Out(c.data()), pred, proj1, proj2);
435         assert(predCount == 4);
436         assert(proj1Count == 4);
437         assert(proj2Count == 4);
438         assert((c == std::array{1, 1, 1, 1}));
439       }
440     }
441   }
442 
443   return true;
444 }
445 
446 template <class In2, class Out, class Sent2 = In2>
test_iterator_in1()447 constexpr void test_iterator_in1() {
448   test_iterators<cpp17_input_iterator<int*>, In2, Out, sentinel_wrapper<cpp17_input_iterator<int*>>, Sent2>();
449   test_iterators<cpp20_input_iterator<int*>, In2, Out, sentinel_wrapper<cpp20_input_iterator<int*>>, Sent2>();
450   test_iterators<forward_iterator<int*>, In2, Out, forward_iterator<int*>, Sent2>();
451   test_iterators<bidirectional_iterator<int*>, In2, Out, bidirectional_iterator<int*>, Sent2>();
452   test_iterators<random_access_iterator<int*>, In2, Out, random_access_iterator<int*>, Sent2>();
453   test_iterators<contiguous_iterator<int*>, In2, Out, contiguous_iterator<int*>, Sent2>();
454   test_iterators<int*, In2, Out, int*, Sent2>();
455   // static_asserting here to avoid hitting the constant evaluation step limit
456   static_assert(test_iterators<cpp17_input_iterator<int*>, In2, Out, sentinel_wrapper<cpp17_input_iterator<int*>>, Sent2>());
457   static_assert(test_iterators<cpp20_input_iterator<int*>, In2, Out, sentinel_wrapper<cpp20_input_iterator<int*>>, Sent2>());
458   static_assert(test_iterators<forward_iterator<int*>, In2, Out, forward_iterator<int*>, Sent2>());
459   static_assert(test_iterators<bidirectional_iterator<int*>, In2, Out, bidirectional_iterator<int*>, Sent2>());
460   static_assert(test_iterators<random_access_iterator<int*>, In2, Out, random_access_iterator<int*>, Sent2>());
461   static_assert(test_iterators<contiguous_iterator<int*>, In2, Out, contiguous_iterator<int*>, Sent2>());
462   static_assert(test_iterators<int*, In2, Out, int*, Sent2>());
463 }
464 
465 template <class Out>
test_iterators_in1_in2()466 void test_iterators_in1_in2() {
467   test_iterator_in1<cpp17_input_iterator<int*>, Out, sentinel_wrapper<cpp17_input_iterator<int*>>>();
468   test_iterator_in1<cpp20_input_iterator<int*>, Out, sentinel_wrapper<cpp20_input_iterator<int*>>>();
469   test_iterator_in1<forward_iterator<int*>, Out>();
470   test_iterator_in1<bidirectional_iterator<int*>, Out>();
471   test_iterator_in1<random_access_iterator<int*>, Out>();
472   test_iterator_in1<contiguous_iterator<int*>, Out>();
473   test_iterator_in1<int*, Out>();
474 }
475 
test()476 constexpr bool test() {
477   { // check that std::ranges::dangling is returned properly
478     { // unary
479       std::array<int, 5> b;
480       std::same_as<std::ranges::in_out_result<std::ranges::dangling, int*>> auto ret =
481           std::ranges::transform(std::array{1, 2, 3, 5, 4}, b.data(), [](int i) { return i * i; });
482       assert((b == std::array{1, 4, 9, 25, 16}));
483       assert(ret.out == b.data() + b.size());
484     }
485     // binary
486     {
487       int b[] = {2, 5, 4, 3, 1};
488       std::array<int, 5> c;
489       std::same_as<std::ranges::in_in_out_result<std::ranges::dangling, int*, int*>> auto ret =
490           std::ranges::transform(std::array{1, 2, 3, 5, 4}, b, c.data(), [](int i, int j) { return i * j; });
491       assert((c == std::array{2, 10, 12, 15, 4}));
492       assert(ret.in2 == b + 5);
493       assert(ret.out == c.data() + c.size());
494     }
495     {
496       int a[] = {2, 5, 4, 3, 1, 4, 5, 6};
497       std::array<int, 8> c;
498       std::same_as<std::ranges::in_in_out_result<int*, std::ranges::dangling, int*>> auto ret =
499           std::ranges::transform(a, std::array{1, 2, 3, 5, 4, 5, 6, 7}, c.data(), [](int i, int j) { return i * j; });
500       assert((c == std::array{2, 10, 12, 15, 4, 20, 30, 42}));
501       assert(ret.in1 == a + 8);
502       assert(ret.out == c.data() + c.size());
503     }
504     {
505       std::array<int, 3> c;
506       std::same_as<std::ranges::in_in_out_result<std::ranges::dangling, std::ranges::dangling, int*>> auto ret =
507           std::ranges::transform(std::array{4, 4, 4}, std::array{4, 4, 4}, c.data(), [](int i, int j) { return i * j; });
508       assert((c == std::array{16, 16, 16}));
509       assert(ret.out == c.data() + c.size());
510     }
511   }
512 
513   { // check that returning another type from the projection works
514     { // unary
515       {
516         struct S { int i; int other; };
517         S a[] = { S{0, 0}, S{1, 0}, S{3, 0}, S{10, 0} };
518         std::array<int, 4> b;
519         std::ranges::transform(a, a + 4, b.begin(), [](S s) { return s.i; });
520         assert((b == std::array{0, 1, 3, 10}));
521       }
522       {
523         struct S { int i; int other; };
524         S a[] = { S{0, 0}, S{1, 0}, S{3, 0}, S{10, 0} };
525         std::array<int, 4> b;
526         std::ranges::transform(a, b.begin(), [](S s) { return s.i; });
527         assert((b == std::array{0, 1, 3, 10}));
528       }
529     }
530     { // binary
531       {
532         struct S { int i; int other; };
533         S a[] = { S{0, 0}, S{1, 0}, S{3, 0}, S{10, 0} };
534         S b[] = { S{0, 10}, S{1, 20}, S{3, 30}, S{10, 40} };
535         std::array<int, 4> c;
536         std::ranges::transform(a, a + 4, b, b + 4, c.begin(), [](S s1, S s2) { return s1.i + s2.other; });
537         assert((c == std::array{10, 21, 33, 50}));
538       }
539       {
540         struct S { int i; int other; };
541         S a[] = { S{0, 0}, S{1, 0}, S{3, 0}, S{10, 0} };
542         S b[] = { S{0, 10}, S{1, 20}, S{3, 30}, S{10, 40} };
543         std::array<int, 4> c;
544         std::ranges::transform(a, b, c.begin(), [](S s1, S s2) { return s1.i + s2.other; });
545         assert((c == std::array{10, 21, 33, 50}));
546       }
547     }
548   }
549 
550   { // check that std::invoke is used
551     { // unary
552       struct S { int i; };
553       S a[] = { S{1}, S{3}, S{2} };
554       std::array<int, 3> b;
555       auto ret = std::ranges::transform(a, b.data(), [](int i) { return i; }, &S::i);
556       assert((b == std::array{1, 3, 2}));
557       assert(ret.out == b.data() + 3);
558     }
559     { // binary
560       struct S { int i; };
561       S a[] = { S{1}, S{3}, S{2} };
562       S b[] = { S{2}, S{5}, S{3} };
563       std::array<int, 3> c;
564       auto ret = std::ranges::transform(a, b, c.data(), [](int i, int j) { return i + j + 2; }, &S::i, &S::i);
565       assert((c == std::array{5, 10, 7}));
566       assert(ret.out == c.data() + 3);
567     }
568   }
569 
570   return true;
571 }
572 
main(int,char **)573 int main(int, char**) {
574   test_iterators_in1_in2<cpp17_output_iterator<int*>>();
575   test_iterators_in1_in2<cpp20_output_iterator<int*>>();
576   test_iterators_in1_in2<forward_iterator<int*>>();
577   test_iterators_in1_in2<bidirectional_iterator<int*>>();
578   test_iterators_in1_in2<random_access_iterator<int*>>();
579   test_iterators_in1_in2<contiguous_iterator<int*>>();
580   test_iterators_in1_in2<int*>();
581   test();
582   static_assert(test());
583 
584   return 0;
585 }
586