std::ranges...form_result,(3)
πΈ std::ranges::transform + std::ranges::unary_transform_result πΈ
okay bestie, so this one is from the C++ Standard Library manual pages (part 1, more coming, don't go anywhere π ). it's giving "apply a function to a range and dump the results somewhere else" and honestly it's iconic.
β¨ NAME β¨
std::ranges::transform, std::ranges::unary_transform_result β the range-transform duo, together at last π«Ά
β¨ SYNOPSIS β¨
so first things first: it's Defined in header <algorithm> π
Call signature
this is overload (1), since C++20:
template< std::input_iterator I, std::sentinel_for<I> S, std::weakly_incrementable O,
std::copy_constructible F, class Proj = std::identity >
requires std::indirectly_writable<O,
std::indirect_result_t<F&,
std::projected<I, Proj>>>
constexpr unary_transform_result<I, O>
transform( I first1, S last1, O result, F op, Proj proj = {} );
overload (2), also since C++20 β the range-y one:
template< ranges::input_range R, std::weakly_incrementable O,
std::copy_constructible F, class Proj = std::identity >
requires std::indirectly_writable<O,
std::indirect_result_t<F&,
std::projected<ranges::iterator_t<R>, Proj>>>
constexpr unary_transform_result<ranges::borrowed_iterator_t<R>, O>
transform( R&& r, O result, F op, Proj proj = {} );
overload (3), since C++20 β now with TWO ranges, look at her go:
template< std::input_iterator I1, std::sentinel_for<I1> S1,
std::input_iterator I2, std::sentinel_for<I2> S2,
std::weakly_incrementable O,
std::copy_constructible F,
class Proj1 = std::identity, class Proj2 = std::identity >
requires std::indirectly_writable<O,
std::indirect_result_t<F&,
std::projected<I1, Proj1>,
std::projected<I2, Proj2>>>
constexpr binary_transform_result<I1, I2, O>
transform( I1 first1, S1 last1, I2 first2, S2 last2, O result,
F binary_op, Proj1 proj1 = {}, Proj2 proj2 = {} );
overload (4), since C++20 β two ranges, both as ranges, full main-character energy π:
template< ranges::input_range R1, ranges::input_range R2,
std::weakly_incrementable O,
std::copy_constructible F,
class Proj1 = std::identity, class Proj2 = std::identity >
requires std::indirectly_writable<O,
std::indirect_result_t<F&,
std::projected<ranges::iterator_t<R1>, Proj1>,
std::projected<ranges::iterator_t<R2>, Proj2>>>
constexpr binary_transform_result<ranges::borrowed_iterator_t<R1>,
ranges::borrowed_iterator_t<R2>, O>
transform( R1&& r1, R2&& r2, O result, F binary_op,
Proj1 proj1 = {}, Proj2 proj2 = {} );
Helper types
(5) since C++20 β the unary one:
template< class I, class O >
using unary_transform_result = ranges::in_out_result<I, O>;
(6) since C++20 β the binary one:
template< class I1, class I2, class O >
using binary_transform_result = ranges::in_in_out_result<I1, I2, O>;
β¨ DESCRIPTION β¨
so what does she DO? she applies the given function to a range and stores the result in another range, beginning at result. that's it, that's the whole vibe β¨
okay but fr fr, here's the breakdown:
- the unary operation
opis applied to the range defined by[first1, last1)β and yes, after projecting with the projectionprojπ - same as (1), but it uses
ras the source range, as if usingranges::begin(r)asfirstandranges::end(r)aslast. convenient, no notes. - the binary operation
binary_opis applied to pairs of elements from two ranges: one defined by[first1, last1)and the other defined by[first2, last2)β after respectively projecting with the projectionsproj1andproj2. two ranges, one function, she ate π₯ - same as (3), but uses
r1as the first source range, as if usingranges::begin(r1)asfirst1andranges::end(r1)aslast1, and similarly forr2. symmetrical slay.
π«Ά real talk: the function-like entities described on this page are niebloids, that is: - explicit template argument lists cannot be specified when calling any of them π - none of them are visible to argument-dependent lookup - when any of them are found by normal unqualified lookup as the name to the left of the function-call operator, argument-dependent lookup is inhibited in practice, they may be implemented as function objects, or with special compiler extensions. that's just how it is, bestie π
β¨ PARAMETERS β¨
first1,last1β the first range of elements to transformr,r1β the first range of elements to transformfirst2,last2β the second range of elements to transformr2β the second range of elements to transformresultβ the beginning of the destination range, may be equal tofirst1orfirst2πop,binary_opβ operation to apply to the projected element(s)proj1β projection to apply to the elements in the first rangeproj2β projection to apply to the elements in the second range
β¨ RETURN VALUE β¨
- & 2. a
unary_transform_resultcontains an input iterator equal tolastand an output iterator to the element past the last element transformed π - & 4. a
binary_transform_resultcontains input iterators to last transformed elements from ranges[first1, last1)and[first2, last2)asin1andin2respectively, and the output iterator to the element past the last element transformed asout. all accounted for π
β¨ COMPLEXITY β¨
- & 2. exactly
ranges::distance(first1, last1)applications ofopandproj. exact. no guessing. β - & 4. exactly
ranges::min(ranges::distance(first1, last1), ranges::distance(first2, last2))applications ofbinary_opand projections. she stops at the shorter one, as a treat πΈ
β¨ POSSIBLE IMPLEMENTATION β¨
ngl this is the fun part, look how clean she is:
struct transform_fn
{
// First version
template<std::input_iterator I, std::sentinel_for<I> S, std::weakly_incrementable O,
std::copy_constructible F, class Proj = std::identity>
requires std::indirectly_writable<O, std::indirect_result_t<F&,
std::projected<I, Proj>>>
constexpr ranges::unary_transform_result<I, O>
operator()(I first1, S last1, O result, F op, Proj proj = {}) const
{
for (; first1 != last1; ++first1, (void)++result)
*result = std::invoke(op, std::invoke(proj, *first1));
return {first1, result};
}
// Second version
template<ranges::input_range R, std::weakly_incrementable O,
std::copy_constructible F, class Proj = std::identity>
requires std::indirectly_writable<O,
std::indirect_result_t<F&, std::projected<ranges::iterator_t<R>, Proj>>>
constexpr ranges::unary_transform_result<ranges::borrowed_iterator_t<R>, O>
operator()(R&& r, O result, F op, Proj proj = {}) const
{
return (*this)(ranges::begin(r), ranges::end(r), result,
std::ref(op), std::ref(proj));
}
// Third version
template<std::input_iterator I1, std::sentinel_for<I1> S1,
std::input_iterator I2, std::sentinel_for<I2> S2,
std::weakly_incrementable O,
std::copy_constructible F,
class Proj1 = std::identity, class Proj2 = std::identity>
requires std::indirectly_writable<O,
std::indirect_result_t<F&,
std::projected<I1, Proj1>,
std::projected<I2, Proj2>>>
constexpr ranges::binary_transform_result<I1, I2, O>
operator()(I1 first1, S1 last1, I2 first2, S2 last2, O result,
F binary_op, Proj1 proj1 = {}, Proj2 proj2 = {}) const
{
for (; first1 != last1 && first2 != last2;
++first1, (void)++first2, (void)++result)
*result = std::invoke(binary_op,
std::invoke(proj1, *first1),
std::invoke(proj2, *first2));
return {first1, first2, result};
}
// Fourth version
template<ranges::input_range R1, ranges::input_range R2,
std::weakly_incrementable O, std::copy_constructible F,
class Proj1 = std::identity, class Proj2 = std::identity>
requires std::indirectly_writable<O,
std::indirect_result_t<F&,
std::projected<ranges::iterator_t<R1>, Proj1>,
std::projected<ranges::iterator_t<R2>, Proj2>>>
constexpr ranges::binary_transform_result<ranges::borrowed_iterator_t<R1>,
ranges::borrowed_iterator_t<R2>, O>
operator()(R1&& r1, R2&& r2, O result,
F binary_op, Proj1 proj1 = {}, Proj2 proj2 = {}) const
{
return (*this)(ranges::begin(r1), ranges::end(r1),
ranges::begin(r2), ranges::end(r2),
result, std::ref(binary_op),
std::ref(proj1), std::ref(proj2));
}
};
inline constexpr transform_fn transform;
β¨ NOTES β¨
β οΈ okay important one:ranges::transformdoes not guarantee in-order application ofoporbinary_op. so if you were counting on order, bestie, don't π
if you need to apply a function to a sequence in-order, or apply a function that modifies the elements of a sequence, use ranges::for_each instead. that's the one you want. periodt π
(part 1 done β part 2 is coming, don't touch that dial π¦)
β¨ EXAMPLE β¨
Okay bestie, this example is literally the moment you've been waiting for π«Ά. The code below uses ranges::transform to convert a std::string in place to uppercase using the std::toupper function, and then it transforms each char to its ordinal value. Then ranges::transform with a projection is used to transform elements of std::vector<Foo> into chars to fill a std::string. No cap, it's giving range-algorithm excellence π
.
// Run this code
#include <algorithm>
#include <cctype>
#include <functional>
#include <iostream>
#include <string>
#include <vector>
int main()
{
std::string s{"hello"};
auto op = [](unsigned char c) -> unsigned char { return std::toupper(c); };
namespace ranges = std::ranges;
// uppercase the string in-place
ranges::transform(s.begin(), s.end(), s.begin(), op );
std::vector<std::size_t> ordinals;
// convert each char to size_t
ranges::transform(s, std::back_inserter(ordinals),
[](unsigned char c) -> std::size_t { return c; });
std::cout << s << ':';
for (auto ord : ordinals)
std::cout << ' ' << ord;
// double each ordinal
ranges::transform(ordinals, ordinals, ordinals.begin(), std::plus {});
std::cout << '\n';
for (auto ord : ordinals)
std::cout << ord << ' ';
std::cout << '\n';
struct Foo
{
char bar;
};
const std::vector<Foo> f = {{'h'},{'e'},{'l'},{'l'},{'o'}};
std::string result;
// project, then uppercase
ranges::transform(f, std::back_inserter(result), op, &Foo::bar);
std::cout << result << '\n';
}
π» OUTPUT π»
And the vibe check passes β here's what it prints:
HELLO: 72 69 76 76 79
144 138 152 152 158
HELLO
Like, the string gets uppercased, the ordinals get printed, then doubled, then the projection magic turns those Foos back into HELLO. Obsessed ππ₯.
π SEE ALSO π
ranges::for_eachβ applies a function to a range of elements (C++20) (niebloid)ranges::transform_view,views::transformβ a view of a sequence that applies a transformation function to each element (C++20) (class template) (range adaptor object)transformβ applies a function to a range of elements, storing results in a destination range (function template)
That's the whole page, bestie π. Source is http://cppreference.com and the version/date on the original footer is 2024.06.10 β so yeah, go slay some ranges, periodt ππ.
π¦ where this page comes from
- nixpkgs unstable: stdman 2024.07.05
- opensuse-leap 16.0: stdman 2024.07.05-bp160.1.9 Β· MIT
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms