std::ranges::generate(3)
β¨ std::ranges::generate β¨
okay so this page lives in the C++ Standard Library bestie, and honestly it's one of those little gems you're gonna use all the time and never think about. it's std::ranges::generate and it's here to fill your range with vibes (and by vibes i mean whatever your generator function spits out) π
πΈ NAME πΈ
std::ranges::generate β literally std::ranges::generate. that's the whole name section. it ate and left no crumbs π
π¦ Synopsis π¦
the header it lives in
Defined in header <algorithm>
Call signature π
template< std::input_or_output_iterator O, std::sentinel_for<O> S,
std::copy_constructible F >
requires std::invocable<F&> && std::indirectly_writable<O, std::invoke_result_t<F&>>
constexpr O generate( O first, S last, F gen );
(1) (since C++20)
template< class R, std::copy_constructible F >
requires std::invocable<F&> && ranges::output_range<R, std::invoke_result_t<F&>>
constexpr ranges::borrowed_iterator_t<R> generate( R&& r, F gen );
(2) (since C++20)
so what do these two actually do π
- assigns the result of successive invocations of the function object
gento each element in the range[first, last). yes bestie, every single element gets a fresh call. no reusing, no recycling, fresh out the oven π₯ - same as (1), but uses
ras the range, as if usingranges::begin(r)asfirstandranges::end(r)aslast. same energy, less typing π«Ά
the niebloid lore π§
the function-like entities described on this page are niebloids, that is:
- explicit template argument lists cannot be specified when calling any of them. nope. not allowed. don't try it π
- 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. either way it's giving "trust the process" β¨
π Parameters π
first,lastβ the range of elements to modify. this is the glow-up zone πrβ the range of elements to modify. same zone, just handed over as a range instead of two iterators πgenβ the generator function object. the main character of this operation, fr fr π
β Return value β
an output iterator that compares equal to last. so you always know where you ended up π§
π Complexity π
exactly ranges::distance(first, last) invocations of gen() and that many assignments. one call, one write, per element. no cap, no extra credit π
π Possible implementation π
this is the vibe, in code form:
struct generate_fn
{
template<std::input_or_output_iterator O, std::sentinel_for<O> S,
std::copy_constructible F>
requires std::invocable<F&> && std::indirectly_writable<O, std::invoke_result_t<F&>>
constexpr O operator()(O first, S last, F gen) const
{
for (; first != last; *first = std::invoke(gen), ++first)
{}
return first;
}
template<class R, std::copy_constructible F>
requires std::invocable<F&> && ranges::output_range<R, std::invoke_result_t<F&>>
constexpr ranges::borrowed_iterator_t<R> operator()(R&& r, F gen) const
{
return (*this)(ranges::begin(r), ranges::end(r), std::move(gen));
}
};
inline constexpr generate_fn generate {};
look at that comma operator in the for loop doing the absolute most. iconic π
π Example π
// Run this code
#include <algorithm>
#include <array>
#include <iostream>
#include <random>
#include <string_view>
auto dice()
{
static std::uniform_int_distribution<int> distr {1, 6};
static std::random_device device;
static std::mt19937 engine {device()};
return distr(engine);
}
void iota(auto& v, int n)
{
std::ranges::generate(v, [&n]() mutable { return n++; });
}
void print(std::string_view comment, const auto& v)
{
for (std::cout << comment; int i : v)
std::cout << i << ' ';
std::cout << '\n';
}
int main()
{
std::array<int, 8> v;
std::ranges::generate(v.begin(), v.end(), dice);
print("dice: ", v);
std::ranges::generate(v, dice);
print("dice: ", v);
iota(v, 1);
print("iota: ", v);
}
Possible output: π²
dice: 4 3 1 6 6 4 5 5
dice: 4 2 5 3 6 2 6 2
iota: 1 2 3 4 5 6 7 8
(yes it's random, your dice rolls will be different, that's the whole point bestie π)
π₯ See also π₯
ranges::generate_n(C++20) (niebloid) β saves the result of N applications of a functionranges::fill(C++20) (niebloid) β assigns a range of elements a certain valueranges::fill_n(C++20) (niebloid) β assigns a value to a number of elementsranges::transform(C++20) (niebloid) β applies a function to a range of elementsranges::generate_random(C++26) (niebloid) β fills a range with random numbers from a uniform random bit generatorgenerate(function template) β assigns the results of successive function calls to every element in a range
that's the whole page, you understood it, you slayed it, and honestly the algorithms library is lucky to have you π«Ά
k bye, go generate something cute πΈ
β source: http://cppreference.com, dated 2024.06.10, and this lil manual page header says std::ranges::generate(3). mwah π
π¦ 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