std::generate_n(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

πŸ’– std::generate_n(3) β€” the C++ Standard Library glow-up πŸ’–

okay so bestie, listen up, because std::generate_n is literally that girl of the <algorithm> header. she fills your range with vibes (well, with values from a generator), and she's been doing it since forever. this page is from the C++ Standard Library manual, and we're gonna stan her properly. periodt.


✨ Synopsis ✨

defined in header <algorithm>, bestie. like, that's her home base.

template< class OutputIt, class Size, class Generator >
OutputIt generate_n( OutputIt first, Size count,          (1)
(constexpr since C++20)
Generator g );

template< class ExecutionPolicy,
          class ForwardIt, class Size, class Generator >
ForwardIt generate_n( ExecutionPolicy&& policy,           (2) (since C++17)
                      ForwardIt first, Size count,
                      Generator g );

1) this overload assigns values generated by the given function object g to the first count elements in the range beginning at first β€” but only if count > 0. if not, she does nothing. literally nothing. she's not gonna waste her energy.

2) same as (1), but executed according to policy.

✨ the tea: this overload participates in overload resolution only if std::is_execution_policy_v<std::decay_t<ExecutionPolicy>> is true (until C++20), or std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> is true (since C++20).

and lowkey, if Size is not convertible to an integral type, the program is ill-formed. no cap, that's a hard no. πŸ’€


πŸŽ€ Parameters πŸŽ€

  • first β€” the beginning of the range of elements to generate. like, where the magic starts.
  • count β€” number of the elements to generate. how many besties are we giving values to?
  • policy β€” the execution policy to use. see execution policy for details (iykyk).
  • g β€” the generator function object that will be called. the signature of the function should be equivalent to the following:
Ret fun();

the type Ret must be such that an object of type OutputIt can be dereferenced and assigned a value of type Ret. so like, the output iterator has to be able to handle whatever g is serving. fr fr.


πŸ’… Type Requirements πŸ’…

  • OutputIt must meet the requirements of LegacyOutputIterator.
  • ForwardIt must meet the requirements of LegacyForwardIterator.

she has standards, okay? don't come at her with anything less. ✨


🌸 Return Value 🌸

an iterator one past the last element assigned if count > 0 β€” and first otherwise. so if you gave her count = 0, she just hands you back first like "here, you can have this back, bestie". πŸ’–


πŸ¦‹ Complexity πŸ¦‹

exactly std::max(0, count) invocations of g() and assignments. not a single one more, not a single one less. she's precise like that. no wasted moves. main character energy. πŸ‘‘


πŸ”₯ Exceptions πŸ”₯

the overload with a template parameter named ExecutionPolicy reports errors as follows:

  • if execution of a function invoked as part of the algorithm throws an exception and ExecutionPolicy is one of the standard policies, std::terminate is called. so like, she will literally end it. dramatic? maybe. true? absolutely. πŸ’€
  • for any other ExecutionPolicy, the behavior is implementation-defined. mysterious. we don't know her like that.
  • if the algorithm fails to allocate memory, std::bad_alloc is thrown. out of memory? she's throwing hands (and bad_alloc). 😭

🌈 Possible Implementation 🌈

like, if you wanted to write her from scratch, this is the vibe:

template<class OutputIt, class Size, class Generator>
constexpr // since C++20
OutputIt generate_n(OutputIt first, Size count, Generator g)
{
    for (Size i = 0; i < count; ++i, ++first)
        *first = g();

    return first;
}

look at her. so simple. so iconic. not a single line wasted. 🫢


πŸ“ Example πŸ“

// Run this code

#include <algorithm>
#include <functional>
#include <iostream>
#include <iterator>
#include <random>

int main()
{
    std::mt19937 rng; // default constructed, seeded with fixed seed
    std::generate_n(std::ostream_iterator<std::mt19937::result_type>(std::cout, " "),
                    5, std::ref(rng));
    std::cout << '\n';
}

Output:

3499211612 581869302 3890346734 3586334585 545404204

like, she literally generated five random numbers and served them on a silver platter. obsessed. πŸ”₯


🧚 Defect Reports 🧚

the following behavior-changing defect reports were applied retroactively to previously published C++ standards. so like, the glow-up was real and it was documented. πŸ’…

  • LWG 426 (C++98) β€” the complexity requirement was "exactly no count invocations or assignments", which is broken if count is negative. correct behavior: "exactly std::max(0, count) invocations or assignments" if count is non-positive. she fixed her math, bestie. we love growth. 🌱
  • LWG 865 (C++98) β€” the location of the first element following the generation range was not returned. correct behavior: first is returned. she was like "oh wait, I forgot to hand that back, my bad" and now she does. πŸ™ˆ

⭐ See Also ⭐

  • fill_n β€” copy-assigns the given value to N elements in a range (function template). she's the "copy-paste the same vibe" queen.
  • generate β€” assigns the results of successive function calls to every element in a range (function template). the OG, the blueprint, the mother.
  • ranges::generate_n (C++20) β€” saves the result of N applications of a function (niebloid). the shiny new C++20 edition. she's the upgraded version and she knows it. ✨

πŸ‘‘ Sign-off πŸ‘‘

this page was brought to you by cppreference.com, version 2024.06.10, and the std::generate_n(3) manual. she's been serving looks and values since C++98 and she's not stopping anytime soon. slay, generate_n. slay. πŸ’–βœ¨πŸŽ€

periodt. πŸ’…

πŸ“¦ where this page comes from