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