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