File: transform.hpp

package info (click to toggle)
range-v3 0.12.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,652 kB
  • sloc: cpp: 76,839; xml: 226; sh: 89; python: 34; makefile: 19; perl: 15
file content (618 lines) | stat: -rw-r--r-- 24,043 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
/// \file
// Range v3 library
//
//  Copyright Eric Niebler 2013-present
//
//  Use, modification and distribution is subject to the
//  Boost Software License, Version 1.0. (See accompanying
//  file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//
// Project home: https://github.com/ericniebler/range-v3
//

#ifndef RANGES_V3_VIEW_TRANSFORM_HPP
#define RANGES_V3_VIEW_TRANSFORM_HPP

#include <iterator>
#include <type_traits>
#include <utility>

#include <meta/meta.hpp>

#include <range/v3/range_fwd.hpp>

#include <range/v3/algorithm/max.hpp>
#include <range/v3/algorithm/min.hpp>
#include <range/v3/functional/bind_back.hpp>
#include <range/v3/functional/indirect.hpp>
#include <range/v3/functional/invoke.hpp>
#include <range/v3/iterator/operations.hpp>
#include <range/v3/range/access.hpp>
#include <range/v3/range/primitives.hpp>
#include <range/v3/range/traits.hpp>
#include <range/v3/utility/move.hpp>
#include <range/v3/utility/semiregular_box.hpp>
#include <range/v3/utility/static_const.hpp>
#include <range/v3/view/adaptor.hpp>
#include <range/v3/view/all.hpp>
#include <range/v3/view/view.hpp>

#include <range/v3/detail/prologue.hpp>

namespace ranges
{
    /// \cond
    namespace detail
    {
        constexpr cardinality transform2_cardinality(cardinality c1, cardinality c2)
        {
            return c1 >= 0 || c2 >= 0
                       ? (c1 >= 0 && c2 >= 0 ? (c1 < c2 ? c1 : c2) : finite)
                       : c1 == finite || c2 == finite
                             ? finite
                             : c1 == unknown || c2 == unknown ? unknown : infinite;
        }

        // clang-format off
        /// \concept iter_transform_1_readable_
        /// \brief The \c iter_transform_1_readable_ concept
        template(typename Fun, typename Rng)(
        concept (iter_transform_1_readable_)(Fun, Rng),
            regular_invocable<Fun &, iterator_t<Rng>> AND
            regular_invocable<Fun &, copy_tag, iterator_t<Rng>> AND
            regular_invocable<Fun &, move_tag, iterator_t<Rng>> AND
            common_reference_with<
                invoke_result_t<Fun &, iterator_t<Rng>> &&,
                invoke_result_t<Fun &, copy_tag, iterator_t<Rng>> &> AND
            common_reference_with<
                invoke_result_t<Fun &, iterator_t<Rng>> &&,
                invoke_result_t<Fun &, move_tag, iterator_t<Rng>> &&> AND
            common_reference_with<
                invoke_result_t<Fun &, move_tag, iterator_t<Rng>> &&,
                invoke_result_t<Fun &, copy_tag, iterator_t<Rng>> const &>
        );
        /// \concept iter_transform_1_readable
        /// \brief The \c iter_transform_1_readable concept
        template<typename Fun, typename Rng>
        CPP_concept iter_transform_1_readable =
            CPP_concept_ref(detail::iter_transform_1_readable_, Fun, Rng);

        /// \concept iter_transform_2_readable_
        /// \brief The \c iter_transform_2_readable_ concept
        template(typename Fun, typename Rng1, typename Rng2)(
        concept (iter_transform_2_readable_)(Fun, Rng1, Rng2),
            regular_invocable<Fun &, iterator_t<Rng1>, iterator_t<Rng2>> AND
            regular_invocable<Fun &, copy_tag, iterator_t<Rng1>, iterator_t<Rng2>> AND
            regular_invocable<Fun &, move_tag, iterator_t<Rng1>, iterator_t<Rng2>> AND
            common_reference_with<
                invoke_result_t<Fun &, iterator_t<Rng1>, iterator_t<Rng2>> &&,
                invoke_result_t<Fun &, copy_tag, iterator_t<Rng1>, iterator_t<Rng2>> &> AND
            common_reference_with<
                invoke_result_t<Fun &, iterator_t<Rng1>, iterator_t<Rng2>> &&,
                invoke_result_t<Fun &, move_tag, iterator_t<Rng1>, iterator_t<Rng2>> &&> AND
            common_reference_with<
                invoke_result_t<Fun &, move_tag, iterator_t<Rng1>, iterator_t<Rng2>> &&,
                invoke_result_t<Fun &, copy_tag, iterator_t<Rng1>, iterator_t<Rng2>> const &>
        );
        /// \concept iter_transform_2_readable
        /// \brief The \c iter_transform_2_readable concept
        template<typename Fun, typename Rng1, typename Rng2>
        CPP_concept iter_transform_2_readable =
            CPP_concept_ref(detail::iter_transform_2_readable_, Fun, Rng1, Rng2);
        // clang-format on
    } // namespace detail
    /// \endcond

    /// \addtogroup group-views
    /// @{
    template<typename Rng, typename Fun>
    struct iter_transform_view : view_adaptor<iter_transform_view<Rng, Fun>, Rng>
    {
    private:
        friend range_access;
        RANGES_NO_UNIQUE_ADDRESS semiregular_box_t<Fun> fun_;
        template<bool Const>
        using use_sentinel_t =
            meta::bool_<!common_range<meta::const_if_c<Const, Rng>> ||
                        single_pass_iterator_<iterator_t<meta::const_if_c<Const, Rng>>>>;

        template<bool IsConst>
        struct adaptor : adaptor_base
        {
        private:
            friend struct adaptor<!IsConst>;
            using CRng = meta::const_if_c<IsConst, Rng>;
            using fun_ref_ = semiregular_box_ref_or_val_t<Fun, IsConst>;
            RANGES_NO_UNIQUE_ADDRESS fun_ref_ fun_;

        public:
            using value_type =
                detail::decay_t<invoke_result_t<Fun &, copy_tag, iterator_t<CRng>>>;
            adaptor() = default;
            adaptor(fun_ref_ fun)
              : fun_(std::move(fun))
            {}
            template(bool Other)(
                requires IsConst AND CPP_NOT(Other)) //
            adaptor(adaptor<Other> that)
              : fun_(std::move(that.fun_))
            {}

            // clang-format off
            auto CPP_auto_fun(read)(iterator_t<CRng> it)(const)
            (
                return invoke(fun_, it)
            )
            auto CPP_auto_fun(iter_move)(iterator_t<CRng> it)(const)
            (
                return invoke(fun_, move_tag{}, it)
            )
            // clang-format on
        };

        adaptor<false> begin_adaptor()
        {
            return {fun_};
        }
        template(bool Const = true)(
            requires Const AND range<meta::const_if_c<Const, Rng>> AND
                detail::iter_transform_1_readable<Fun const,
                                                  meta::const_if_c<Const, Rng>>)
        adaptor<Const> begin_adaptor() const
        {
            return {fun_};
        }
        meta::if_<use_sentinel_t<false>, adaptor_base, adaptor<false>> end_adaptor()
        {
            return {fun_};
        }
        template(bool Const = true)(
            requires Const AND range<meta::const_if_c<Const, Rng>> AND
                    detail::iter_transform_1_readable<Fun const,
                                                      meta::const_if_c<Const, Rng>>)
        meta::if_<use_sentinel_t<Const>, adaptor_base, adaptor<Const>> end_adaptor() const
        {
            return {fun_};
        }

    public:
        iter_transform_view() = default;
        iter_transform_view(Rng rng, Fun fun)
          : iter_transform_view::view_adaptor{std::move(rng)}
          , fun_(std::move(fun))
        {}
        CPP_auto_member
        constexpr auto CPP_fun(size)()(
            requires sized_range<Rng>)
        {
            return ranges::size(this->base());
        }
        CPP_auto_member
        constexpr auto CPP_fun(size)()(const //
            requires sized_range<Rng const>)
        {
            return ranges::size(this->base());
        }
    };

    template<typename Rng, typename Fun>
    struct transform_view : iter_transform_view<Rng, indirected<Fun>>
    {
        transform_view() = default;
        transform_view(Rng rng, Fun fun)
          : iter_transform_view<Rng, indirected<Fun>>{std::move(rng),
                                                      indirect(std::move(fun))}
        {}
    };

#if RANGES_CXX_DEDUCTION_GUIDES >= RANGES_CXX_DEDUCTION_GUIDES_17
    template(typename Rng, typename Fun)(
        requires copy_constructible<Fun>)
    transform_view(Rng &&, Fun)
        -> transform_view<views::all_t<Rng>, Fun>;
#endif

    template<typename Rng1, typename Rng2, typename Fun>
    struct iter_transform2_view
      : view_facade<iter_transform2_view<Rng1, Rng2, Fun>,
                    detail::transform2_cardinality(range_cardinality<Rng1>::value,
                                                   range_cardinality<Rng2>::value)>
    {
    private:
        friend range_access;
        RANGES_NO_UNIQUE_ADDRESS semiregular_box_t<Fun> fun_;
        Rng1 rng1_;
        Rng2 rng2_;
        using difference_type_ =
            common_type_t<range_difference_t<Rng1>, range_difference_t<Rng2>>;

        static constexpr cardinality my_cardinality = detail::transform2_cardinality(
            range_cardinality<Rng1>::value, range_cardinality<Rng2>::value);

        template<bool>
        struct cursor;

        template<bool Const>
        struct sentinel
        {
        private:
            friend struct cursor<Const>;
            sentinel_t<meta::const_if_c<Const, Rng1>> end1_;
            sentinel_t<meta::const_if_c<Const, Rng2>> end2_;

        public:
            sentinel() = default;
            sentinel(meta::const_if_c<Const, iter_transform2_view> * parent,
                     decltype(ranges::end))
              : end1_(end(parent->rng1_))
              , end2_(end(parent->rng2_))
            {}
            template(bool Other)(
                requires Const AND CPP_NOT(Other)) //
            sentinel(sentinel<Other> that)
              : end1_(std::move(that.end1_))
              , end2_(std::move(that.end2_))
            {}
        };

        template<bool Const>
        struct cursor
        {
        private:
            using fun_ref_ = semiregular_box_ref_or_val_t<Fun, Const>;
            using R1 = meta::const_if_c<Const, Rng1>;
            using R2 = meta::const_if_c<Const, Rng2>;
            fun_ref_ fun_;
            iterator_t<R1> it1_;
            iterator_t<R2> it2_;

        public:
            using difference_type = difference_type_;
            using single_pass = meta::or_c<(bool)single_pass_iterator_<iterator_t<R1>>,
                                           (bool)single_pass_iterator_<iterator_t<R2>>>;
            using value_type =
                detail::decay_t<invoke_result_t<meta::const_if_c<Const, Fun> &, copy_tag,
                                                iterator_t<R1>, iterator_t<R2>>>;

            cursor() = default;
            template<typename BeginEndFn>
            cursor(meta::const_if_c<Const, iter_transform2_view> * parent,
                   BeginEndFn begin_end)
              : fun_(parent->fun_)
              , it1_(begin_end(parent->rng1_))
              , it2_(begin_end(parent->rng2_))
            {}
            template(bool Other)(
                requires Const AND CPP_NOT(Other)) //
            cursor(cursor<Other> that)
              : fun_(std::move(that.fun_))
              , it1_(std::move(that.end1_))
              , it2_(std::move(that.end2_))
            {}
            // clang-format off
            auto CPP_auto_fun(read)()(const)
            (
                return invoke(fun_, it1_, it2_)
            )
                // clang-format on
                void next()
            {
                ++it1_;
                ++it2_;
            }
            CPP_member
            auto equal(cursor const & that) const //
                -> CPP_ret(bool)(
                    requires forward_range<Rng1> && forward_range<Rng2>)
            {
                // By returning true if *any* of the iterators are equal, we allow
                // transformed ranges to be of different lengths, stopping when the first
                // one reaches the last.
                return it1_ == that.it1_ || it2_ == that.it2_;
            }
            bool equal(sentinel<Const> const & s) const
            {
                // By returning true if *any* of the iterators are equal, we allow
                // transformed ranges to be of different lengths, stopping when the first
                // one reaches the last.
                return it1_ == s.end1_ || it2_ == s.end2_;
            }
            CPP_member
            auto prev() //
                -> CPP_ret(void)(
                    requires bidirectional_range<R1> && bidirectional_range<R2>)
            {
                --it1_;
                --it2_;
            }
            CPP_member
            auto advance(difference_type n) -> CPP_ret(void)(
                requires random_access_range<R1> && random_access_range<R2>)
            {
                ranges::advance(it1_, n);
                ranges::advance(it2_, n);
            }
            CPP_member
            auto distance_to(cursor const & that) const //
                -> CPP_ret(difference_type)(
                    requires sized_sentinel_for<iterator_t<R1>, iterator_t<R1>> &&
                        sized_sentinel_for<iterator_t<R2>, iterator_t<R2>>)
            {
                // Return the smallest distance (in magnitude) of any of the iterator
                // pairs. This is to accommodate zippers of sequences of different length.
                difference_type d1 = that.it1_ - it1_, d2 = that.it2_ - it2_;
                return 0 < d1 ? ranges::min(d1, d2) : ranges::max(d1, d2);
            }
            // clang-format off
            auto CPP_auto_fun(move)()(const)
            (
                return invoke(fun_, move_tag{}, it1_, it2_)
            )
            // clang-format on
        };

        template<bool Const>
        using end_cursor_t = meta::if_c<
            common_range<meta::const_if_c<Const, Rng1>> &&
                common_range<meta::const_if_c<Const, Rng2>> &&
                !single_pass_iterator_<iterator_t<meta::const_if_c<Const, Rng1>>> &&
                !single_pass_iterator_<iterator_t<meta::const_if_c<Const, Rng2>>>,
            cursor<Const>, sentinel<Const>>;

        cursor<simple_view<Rng1>() && simple_view<Rng2>()> begin_cursor()
        {
            return {this, ranges::begin};
        }
        end_cursor_t<simple_view<Rng1>() && simple_view<Rng2>()> end_cursor()
        {
            return {this, ranges::end};
        }
        template(bool Const = true)(
            requires Const AND range<meta::const_if_c<Const, Rng1>> AND
                range<meta::const_if_c<Const, Rng2>> AND
                detail::iter_transform_2_readable< //
                    Fun const, //
                    meta::const_if_c<Const, Rng1>, //
                    meta::const_if_c<Const, Rng2>>)
        cursor<true> begin_cursor() const
        {
            return {this, ranges::begin};
        }
        template(bool Const = true)(
            requires Const AND range<meta::const_if_c<Const, Rng1>> AND
                range<meta::const_if_c<Const, Rng2>> AND
                detail::iter_transform_2_readable< //
                    Fun const, //
                    meta::const_if_c<Const, Rng1>, //
                    meta::const_if_c<Const, Rng2>>)
        end_cursor_t<Const> end_cursor() const
        {
            return {this, ranges::end};
        }
        template<typename Self>
        static constexpr auto size_(Self & self)
        {
            using size_type = common_type_t<range_size_t<Rng1>, range_size_t<Rng2>>;
            return ranges::min(static_cast<size_type>(ranges::size(self.rng1_)),
                               static_cast<size_type>(ranges::size(self.rng2_)));
        }

        template<bool B>
        using R1 = meta::invoke<detail::dependent_<B>, Rng1>;
        template<bool B>
        using R2 = meta::invoke<detail::dependent_<B>, Rng2>;

    public:
        iter_transform2_view() = default;
        constexpr iter_transform2_view(Rng1 rng1, Rng2 rng2, Fun fun)
          : fun_(std::move(fun))
          , rng1_(std::move(rng1))
          , rng2_(std::move(rng2))
        {}
        CPP_member
        static constexpr auto size() //
            -> CPP_ret(std::size_t)(
                requires (my_cardinality >= 0))
        {
            return static_cast<std::size_t>(my_cardinality);
        }
        template(bool True = true)(
            requires (my_cardinality < 0) AND sized_range<Rng1 const> AND
            sized_range<Rng2 const> AND
            common_with<range_size_t<R1<True>>, range_size_t<R2<True>>>)
            constexpr auto size() const
        {
            return size_(*this);
        }
        template(bool True = true)(
            requires (my_cardinality < 0) AND sized_range<Rng1> AND sized_range<Rng2> AND
            common_with<range_size_t<R1<True>>, range_size_t<R2<True>>>)
            constexpr auto size()
        {
            return size_(*this);
        }
    };

    template<typename Rng1, typename Rng2, typename Fun>
    struct transform2_view : iter_transform2_view<Rng1, Rng2, indirected<Fun>>
    {
        transform2_view() = default;
        constexpr transform2_view(Rng1 rng1, Rng2 rng2, Fun fun)
          : iter_transform2_view<Rng1, Rng2, indirected<Fun>>{std::move(rng1),
                                                              std::move(rng2),
                                                              indirect(std::move(fun))}
        {}
    };

    namespace views
    {
        struct iter_transform_base_fn
        {
            template(typename Rng, typename Fun)(
                requires viewable_range<Rng> AND input_range<Rng> AND
                    copy_constructible<Fun> AND
                    detail::iter_transform_1_readable<Fun, Rng>)
            constexpr iter_transform_view<all_t<Rng>, Fun> //
            operator()(Rng && rng, Fun fun) const
            {
                return {all(static_cast<Rng &&>(rng)), std::move(fun)};
            }

            template(typename Rng1, typename Rng2, typename Fun)(
                requires viewable_range<Rng1> AND input_range<Rng1> AND
                    viewable_range<Rng2> AND input_range<Rng2> AND
                    copy_constructible<Fun> AND
                    common_with<range_difference_t<Rng1>, range_difference_t<Rng1>> AND
                    detail::iter_transform_2_readable<Fun, Rng1, Rng2>)
            constexpr iter_transform2_view<all_t<Rng1>, all_t<Rng2>, Fun> //
            operator()(Rng1 && rng1, Rng2 && rng2, Fun fun) const
            {
                return {all(static_cast<Rng1 &&>(rng1)),
                        all(static_cast<Rng2 &&>(rng2)),
                        std::move(fun)};
            }
        };

        struct iter_transform_fn : iter_transform_base_fn
        {
            using iter_transform_base_fn::operator();

            template<typename Fun>
            constexpr auto operator()(Fun fun) const
            {
                return make_view_closure(
                    bind_back(iter_transform_base_fn{}, std::move(fun)));
            }
        };

        /// \relates iter_transform_fn
        /// \ingroup group-views
        RANGES_INLINE_VARIABLE(iter_transform_fn, iter_transform)

        // Don't forget to update views::for_each whenever this set
        // of concepts changes
        // clang-format off
        /// \concept transformable_range_
        /// \brief The \c transformable_range_ concept
        template(typename Rng, typename Fun)(
        concept (transformable_range_)(Rng, Fun),
            regular_invocable<Fun &, range_reference_t<Rng>> AND
            (!std::is_void<indirect_result_t<Fun &, iterator_t<Rng>>>::value)
        );
        /// \concept transformable_range
        /// \brief The \c transformable_range concept
        template<typename Rng, typename Fun>
        CPP_concept transformable_range =
            viewable_range<Rng> && input_range<Rng> &&
            copy_constructible<Fun> &&
            CPP_concept_ref(views::transformable_range_, Rng, Fun);

        /// \concept transformable_ranges_
        /// \brief The \c transformable_ranges_ concept
        template(typename Rng1, typename Rng2, typename Fun)(
        concept (transformable_ranges_)(Rng1, Rng2, Fun),
            regular_invocable<Fun &, range_reference_t<Rng1>, range_reference_t<Rng2>> AND
            (!std::is_void<
                indirect_result_t<Fun &, iterator_t<Rng1>, iterator_t<Rng2>>>::value)
        );
        /// \concept transformable_ranges
        /// \brief The \c transformable_ranges concept
        template<typename Rng1, typename Rng2, typename Fun>
        CPP_concept transformable_ranges =
            viewable_range<Rng1> && input_range<Rng1> &&
            viewable_range<Rng2> && input_range<Rng2> &&
            copy_constructible<Fun> &&
            CPP_concept_ref(views::transformable_ranges_, Rng1, Rng2, Fun);
        // clang-format on

        struct transform_base_fn
        {
            template(typename Rng, typename Fun)(
                requires transformable_range<Rng, Fun>)
            constexpr transform_view<all_t<Rng>, Fun> operator()(Rng && rng, Fun fun)
                const
            {
                return {all(static_cast<Rng &&>(rng)), std::move(fun)};
            }

            template(typename Rng1, typename Rng2, typename Fun)(
                requires transformable_ranges<Rng1, Rng2, Fun>)
            constexpr transform2_view<all_t<Rng1>, all_t<Rng2>, Fun> //
            operator()(Rng1 && rng1, Rng2 && rng2, Fun fun) const
            {
                return {all(static_cast<Rng1 &&>(rng1)),
                        all(static_cast<Rng2 &&>(rng2)),
                        std::move(fun)};
            }
        };

        /// # ranges::views::transform
        /// The transform view takes in a function `T -> U` and converts an input
        /// range of `T` into an output range of `U` by calling the function on every
        /// element of the input range.
        ///
        /// ## Example
        /// \snippet example/view/transform.cpp transform example
        ///
        /// ### Output
        /// \include example/view/transform_golden.txt
        ///
        /// ## Syntax
        /// ```cpp
        /// auto output_range = input_range | ranges::views::transform(transform_func);
        /// ```
        ///
        /// ## Parameters
        /// <pre><b>transform_func</b></pre>
        ///   - Maps an input value to an output value (`transform_func(T) -> U`)
        ///
        /// <pre><b>input_range</b></pre>
        ///   - The range of elements to transform
        ///   - Reference type: `T`
        ///
        /// <pre><b>output_range</b></pre>
        ///   - The range of output values
        ///   - Reference type: `U`
        ///   - Value type: `decay_t<U>`
        ///   - This range will have the same category as the input range (excluding
        ///   contiguous ranges). Contiguous ranges are reduced to random access ranges.
        ///
        struct transform_fn : transform_base_fn
        {
            using transform_base_fn::operator();

            template<typename Fun>
            constexpr auto operator()(Fun fun) const
            {
                return make_view_closure(bind_back(transform_base_fn{}, std::move(fun)));
            }
        };

        /// \relates transform_fn
        /// \ingroup group-views
        RANGES_INLINE_VARIABLE(transform_fn, transform)
    } // namespace views

    namespace cpp20
    {
        namespace views
        {
            using ranges::views::transform;
        }
        template(typename Rng, typename F)(
            requires input_range<Rng> AND copy_constructible<F> AND view_<Rng> AND
                std::is_object<F>::value AND
                    regular_invocable<F &, iter_reference_t<iterator_t<Rng>>>)
            using transform_view = ranges::transform_view<Rng, F>;
    } // namespace cpp20
    /// @}
} // namespace ranges

#include <range/v3/detail/epilogue.hpp>

#include <range/v3/detail/satisfy_boost_range.hpp>
RANGES_SATISFY_BOOST_RANGE(::ranges::iter_transform_view)
RANGES_SATISFY_BOOST_RANGE(::ranges::transform_view)

#endif