{"id":35153,"date":"2025-02-24T17:03:51","date_gmt":"2025-02-24T17:03:51","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/cppblog\/?p=35153"},"modified":"2025-02-24T17:06:35","modified_gmt":"2025-02-24T17:06:35","slug":"std-generator-standard-library-coroutine-support","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cppblog\/std-generator-standard-library-coroutine-support\/","title":{"rendered":"std::generator: Standard Library Coroutine Support"},"content":{"rendered":"<p><code><a href=\"https:\/\/en.cppreference.com\/w\/cpp\/coroutine\/generator\">std::generator<\/a><\/code> is a C++23 feature that enables you to write concise, straightforward functions that generate sequences of values on-demand without manually managing state. It builds upon C++20\u2019s <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/language\/coroutines\">coroutines<\/a>, providing some standard library support for this powerful, but complex, language feature. As of Visual Studio 2022 version 17.13, we ship an implementation of <code>std::generator<\/code> in our <a href=\"https:\/\/github.com\/microsoft\/STL\/wiki\/Changelog#vs-2022-1713\">standard library<\/a>.<\/p>\n<p>This blog post will walk through an example of how to use the feature, compare it to implementing custom ranges, consider some of the design decisions made, and briefly look at the performance implications.<\/p>\n<h2>Motivating Example<\/h2>\n<p>Here\u2019s a short motivating example from <a href=\"https:\/\/wg21.link\/p2502r2\">P2502<\/a>, which proposed the feature:<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">std::generator&lt;int&gt; fibonacci() {\r\n    int a = 0, b = 1;\r\n    while (true) {\r\n        co_yield std::exchange(a, std::exchange(b, a + b));\r\n    }\r\n}\r\n\r\nint answer_to_the_universe() {\r\n    auto rng = fibonacci() | std::views::drop(6) | std::views::take(3);\r\n    return std::ranges::fold_left(std::move(rng), 0, std::plus{});\r\n}<\/code><\/pre>\n<p>Here, <code>fibonacci<\/code> is a coroutine, because it uses the <code>co_yield<\/code> keyword. Coroutines are a generalization of a subroutine. A subroutine begins its execution when it is called and completes its execution when it returns to the caller (forgetting about things like exceptions for simplicity). A coroutine\u2019s execution similarly begins when it is called, but it need not complete execution to return control flow back to the caller: it can <em>suspend<\/em> its execution and be resumed later.<\/p>\n<p>Note that, in the above example, <code>fibonacci<\/code> contains an infinite loop. This is not a problem, because when the <code>co_yield<\/code> statement is executed, the coroutine <em>yields<\/em> both control flow and a value back to the caller, suspending the coroutine&#8217;s execution. <code><a href=\"https:\/\/en.cppreference.com\/w\/cpp\/utility\/exchange\">std::exchange<\/a><\/code> is a small helper utility that assigns a new value to a variable and returns the old one.<\/p>\n<p>The behavior of operations like <code>co_yield<\/code> in a coroutine are determined by the coroutine\u2019s return type, in this case <code>std::generator<\/code>.<\/p>\n<p>The <code>answer_to_the_universe<\/code> function uses the <code>std::generator<\/code> object returned by <code>fibonacci<\/code> in tandem with range adaptors. It <em>pipes<\/em> the generator into two range adaptors. The first says to drop the first 6 elements from the range, while the second says to take the next 3 elements. Note that these range adaptors are not executed eagerly; elements are only dropped or taken when a value is requested from the resulting range.<\/p>\n<p>After building a range to operate on, the <code>answer_to_the_universe<\/code> function sums the values in the range using the <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/algorithm\/ranges\/fold_left\"><code>std::ranges::fold_left<\/code><\/a> algorithm. This is a range-based version of <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/algorithm\/accumulate\"><code>std::accumulate<\/code><\/a>. Folding over the values of the range will continually request values from the range, which will generate those values by resuming execution of the coroutine and forwarding on the yielded results. At the end, we end up with the <a href=\"https:\/\/hitchhikers.fandom.com\/wiki\/42\">obvious<\/a> answer: 42.<\/p>\n<h2>Comparison With Custom Ranges<\/h2>\n<p>Let\u2019s look at how <code>std::generator<\/code> makes it much easier to write code that generates sequences of values that are compatible with the ranges algorithms and views in the standard library.<\/p>\n<p>Say we want to represent an infinite range of random numbers using the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Xorshift\">xorshift<\/a> <a href=\"https:\/\/en.wikipedia.org\/wiki\/Pseudorandom_number_generator\">pseudorandom number generator<\/a> (PRNG), which looks like this:<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">std::uint64_t xorshift(std::uint64_t state) {\r\n\u00a0 \u00a0 state ^= state &lt;&lt; 13;\r\n\u00a0 \u00a0 state ^= state &gt;&gt; 7;\r\n\u00a0 \u00a0 state ^= state &lt;&lt; 17;\r\n\u00a0 \u00a0 return state;\r\n}<\/code><\/pre>\n<p>Given the current state of the PRNG, which is a 64-bit integer, <code>xorshift(state)<\/code> generates the next random number in the sequence. Don\u2019t ask me where those numbers come from, they\u2019re magic.<\/p>\n<p>Writing a custom range type for this is achievable, but it is quite verbose and requires a somewhat in-depth understanding of ranges. Let\u2019s give it a shot.<\/p>\n<p>We\u2019ll write a type called <code>xorshift_range<\/code>, which we should be able to use like this:<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">int main() {\r\n\u00a0 \u00a0 auto is_even = [](auto i) { return i % 2 == 0; };\r\n\u00a0 \u00a0 auto ten_even_numbers = xorshift_generator(std::random_device{}())\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 | std::views::filter(is_even)\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 | std::views::take(10);\r\n\u00a0 \u00a0 for (auto i : ten_even_numbers) {\r\n\u00a0 \u00a0 \u00a0 \u00a0 std::cout &lt;&lt; i &lt;&lt; ' ';\r\n\u00a0 \u00a0 }\r\n}<\/code><\/pre>\n<p>The constructor should take a seed for the PRNG, and we should be able to pipe the result into existing range adaptors to, for example, retrieve ten even random numbers.<\/p>\n<p>Let\u2019s start with a definition for the type:<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">class xorshift_range {\r\npublic:\r\n\u00a0 \u00a0 explicit xorshift_range(std::uint64_t seed)\r\n\u00a0 \u00a0 \u00a0 \u00a0 : seed_(seed) {\r\n\u00a0 \u00a0 }\r\n\u00a0 \u00a0 class iterator;\r\n\r\n\u00a0 \u00a0 iterator begin() const;\r\n\u00a0 \u00a0 std::unreachable_sentinel_t end() const {\r\n\u00a0 \u00a0 \u00a0 \u00a0 return {};\r\n\u00a0 \u00a0 }\r\nprivate:\r\n\u00a0 \u00a0 std::uint64_t seed_;\r\n};<\/code><\/pre>\n<p>Reasonably straightforward for now. The constructor takes a seed and stores it in a member. We declare an iterator type, which will implement the generation logic, and a <code>begin<\/code> function that will return an instance of the iterator type. The iterator will be infinite, so it will always compare not equal to <code>end()<\/code>. As such, end returns a <code>std::unreachable_sentinel_t<\/code>, which compares not equal to everything.<\/p>\n<p>The implementation of the iterator type is more complex, so we\u2019ll take it a bit at a time.<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">class xorshift_range::iterator {\r\npublic:\r\n\u00a0 \u00a0 using difference_type = std::ptrdiff_t;\r\n\u00a0 \u00a0 using value_type = std::uint64_t;\r\n\u00a0 \u00a0 using iterator_concept = std::forward_iterator_tag;<\/code><\/pre>\n<p>We first provide some member types to indicate some information about our iterator. We won\u2019t use <code>difference_type<\/code> in the implementation of the iterator, but it\u2019s required for compatibility with ranges, so we pick a reasonable default of <code>std::ptrdiff_t<\/code>. <code>value_type<\/code> is <code>std::uint64_t<\/code> becuause we produce unsigned 64-bit integers. Our iterator cannot move backwards, but you can copy instances of it and increment them independently, which corresponds to <code><a href=\"https:\/\/en.cppreference.com\/w\/cpp\/iterator\/forward_iterator\">std::forward_iterator<\/a><\/code>, so we set <code>iterator_concept<\/code> to <code>std::forward_iterator_tag<\/code>.<\/p>\n<p>Next come the constructors:<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">\u00a0 \u00a0 iterator() = default;\r\n\u00a0 \u00a0 explicit iterator(std::uint64_t seed)\r\n\u00a0 \u00a0 \u00a0 \u00a0 : state_(seed) {\r\n\u00a0 \u00a0 \u00a0 \u00a0 ++(*this);\r\n\u00a0 \u00a0 }<\/code><\/pre>\n<p>C++20 iterators must be default-constructible, so we define a zero-argument constructor. We also define a constructor that takes a seed, stores it in a <code>state_<\/code> member (which we\u2019ll define soon), and drives the iterator forward to generate the first random number.<\/p>\n<p>The operator overloads deal with generating the next number and returning it to client code:<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">\u00a0 \u00a0 std::uint64_t operator*() const {\r\n\u00a0 \u00a0 \u00a0 \u00a0 return state_;\r\n\u00a0 \u00a0 }\r\n\u00a0 \u00a0 iterator&amp; operator++() {\r\n\u00a0 \u00a0 \u00a0 \u00a0 state_ = xorshift(state_);\r\n\u00a0 \u00a0 \u00a0 \u00a0 return *this;\r\n\u00a0 \u00a0 }\r\n\u00a0 \u00a0 iterator operator++(int) {\r\n\u00a0 \u00a0 \u00a0 \u00a0 auto copy = *this;\r\n\u00a0 \u00a0 \u00a0 \u00a0 ++(*this);\r\n\u00a0 \u00a0 \u00a0 \u00a0 return copy;\r\n\u00a0 \u00a0 }<\/code><\/pre>\n<p>The dereference operator returns the current number. The preincrement operator generates the next random number in the sequence using the <code>xorshift<\/code> function we saw earlier. We implement the postincrement operator in terms of the prefix one.<\/p>\n<p>We also need equality operators and the <code>state_<\/code> member:<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">\u00a0 \u00a0 friend bool operator==(const iterator&amp;, const iterator&amp;) = default;\r\nprivate:\r\n\u00a0 \u00a0 std::uint64_t state_ = 0;\r\n};<\/code><\/pre>\n<p>The <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/language\/default_comparisons\">default equality operator<\/a> will check equality based on the current state.<\/p>\n<p>Finally, we need the implementation of begin and one more piece of ranges magic:<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">xorshift_range::iterator xorshift_range::begin() const {\r\n\u00a0 \u00a0 return iterator{seed_};\r\n}\r\nnamespace\u00a0std::ranges\u00a0{\r\n\u00a0\u00a0\u00a0\u00a0template&lt;&gt;\r\n\u00a0\u00a0\u00a0\u00a0constexpr\u00a0bool\u00a0enable_borrowed_range&lt;xorshift_range&gt;\u00a0=\u00a0true;\r\n}<\/code><\/pre>\n<p>The <code>begin<\/code> function just returns an iterator with the supplied seed. The specialization of <code>std::ranges::enable_borrowed_range<\/code> allows us to use <code>xorshift_range<\/code> as an <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/language\/value_category\">rvalue<\/a> in some contexts, because the iterators can safely outlive the parent <code>xorshift_range<\/code> object without any dangling references.<\/p>\n<p>Phew, that was a lot of work. Want to see the <code>std::generator<\/code> version?<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">std::generator&lt;std::uint64_t&gt; xorshift_generator(std::uint64_t seed) {\r\n\u00a0 \u00a0 while (true) {\r\n\u00a0 \u00a0 \u00a0 \u00a0 seed = xorshift(seed);\r\n\u00a0 \u00a0 \u00a0 \u00a0 co_yield seed;\r\n\u00a0 \u00a0 }\r\n}<\/code><\/pre>\n<p>That\u2019s it. That\u2019s the entire code.<\/p>\n<p>It\u2019s not entirely the same as the range version, but is clearly a lot easier to implement and maintain. Here are a few differences:<\/p>\n<ul>\n<li><code>xorshift_range<\/code> models <code><a href=\"https:\/\/learn.microsoft.com\/cpp\/standard-library\/range-concepts#borrowed_range\">std::ranges::borrowed_range<\/a><\/code>, but <code>xorshift_generator<\/code> does not.<\/li>\n<li><code>xorshift_range<\/code> models <code><a href=\"https:\/\/learn.microsoft.com\/cpp\/standard-library\/range-concepts#forward_range\">std::ranges::forward_range<\/a><\/code>, but <code>xorshift_generator<\/code> only models <code><a href=\"https:\/\/learn.microsoft.com\/cpp\/standard-library\/range-concepts#input_range\">std::ranges::input_range<\/a><\/code>.<\/li>\n<li><code>xorshift_range<\/code> is copyable, but <code>xorshift_generator<\/code> is move-only.<\/li>\n<\/ul>\n<p>We\u2019ll look at the reasons behind these shortly. Whether these tradeoffs are worth it for you depends on your use cases.<\/p>\n<h2>Design Decisions Taken<\/h2>\n<p>There are several potential designs for a <code>std::generator<\/code>-like type, all of which have different tradeoffs and knock-on effects on how the type can be used.<\/p>\n<h3>Copyability<\/h3>\n<p>Specializations of <code>std::generator<\/code> and their iterators are move-only. This is because a coroutine\u2019s state is a unique resource: you cannot perform a deep copy of a coroutine, and resuming a coroutine through one handle to it will be visible through all other handles.<\/p>\n<h3>Range and Iterator Category<\/h3>\n<p>As another consequence of a coroutine\u2019s state being a unique resource, specializations of <code>std::generator<\/code> model <code>std::ranges::input_range<\/code>, and their iterators model <code>std::input_iterator<\/code>. This means that <code>std::generators<\/code> are single-pass ranges, which limits the set of range adaptors and algorithms that you can use with them. For example, you cannot pass a generator to <code><a href=\"https:\/\/en.cppreference.com\/w\/cpp\/algorithm\/ranges\/max_element\">std::ranges::max_element<\/a><\/code>, because it requires a forward range.<\/p>\n<h3>Synchronicity<\/h3>\n<p><code>std::generator<\/code> is synchronous. This means that you cannot <code><a href=\"https:\/\/en.cppreference.com\/w\/cpp\/language\/coroutines#co_await\">co_await<\/a><\/code> an asynchronous operation inside the body of the coroutine that returns a <code>std::generator<\/code>.<\/p>\n<h3>Recursiveness<\/h3>\n<p><code>std::generator<\/code> is recursive in that a coroutine that returns a specialization of <code>std::generator<\/code> may yield a <code>std::generator<\/code> of the same type directly rather than having to yield its values one-by-one. It\u2019s easier to understand with an example.<\/p>\n<p>Say we have a binary tree that stores integers, and we want to yield all of its elements in order. We might write code like this:<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">struct tree {\r\n    tree* left;\r\n    tree* right;\r\n    int value;\r\n};\r\nstd::generator&lt;int&gt; yield_all_loop(const tree&amp; t) {\r\n    if (t.left) {\r\n        for (auto l : yield_all_loop(*t.left)) {\r\n            co_yield l;\r\n        }\r\n    }\r\n\r\n    co_yield t.value;\r\n    \r\n    if (t.right) {\r\n        for (auto r : yield_all_loop(*t.right)) {\r\n            co_yield r;\r\n        }\r\n    }\r\n}<\/code><\/pre>\n<p>We recursively call <code>yield_all_loop<\/code> on the left and right subtrees and loop over the elements of each, yielding their values. This results in the number of coroutine resumptions\/suspensions growing linearly with the depth of the tree. That is, if all branches of the tree are the same size and the tree has a depth of 16, then 16 coroutines will be resumed and suspended every time a value is requested. That\u2019s a lot of overhead.<\/p>\n<p><code>std::generator<\/code> allows you to yield the generators for the subtrees directly rather than having to manually yield their values all the way up the call stack. This is supported by the <code><a href=\"https:\/\/en.cppreference.com\/w\/cpp\/ranges\/elements_of\">std::ranges::elements_of<\/a><\/code> type, which doesn\u2019t do anything other than indicate that we want to yield the elements of a generator rather than the generator itself. The code looks like this:<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">std::generator&lt;int&gt; yield_all_direct(const tree&amp; t) {\r\n    if (t.left) {\r\n         co_yield std::ranges::elements_of(yield_all_direct(*t.left));\r\n    }\r\n    co_yield t.value;\r\n    if (t.right) {\r\n        co_yield std::ranges::elements_of(yield_all_direct(*t.right));\r\n    }\r\n}<\/code><\/pre>\n<p>This eliminates the linear growth issue. On my laptop, with a full tree with a depth of 16, this version runs twice as fast as the loop-based version.<\/p>\n<h3>Reference\/Value Type Shenanigans<\/h3>\n<p>The design space for deciding what <code><a href=\"https:\/\/learn.microsoft.com\/cpp\/standard-library\/ranges-alias-templates#range_reference_t\">std::ranges::range_reference_t<\/a><\/code> and <code><a href=\"https:\/\/learn.microsoft.com\/cpp\/standard-library\/ranges-alias-templates#range_value_t\">std::ranges::range_value_t<\/a><\/code> should be for a generator type and how these can be customized is surprisingly complex. This is because we want to support yielding and dereferencing to both reference and value types, while avoiding both dangling references and unnecessary copies. Ideally, we also shouldn\u2019t need to always specify the first template parameter as a reference type, i.e. <code>std::generator&lt;std::string&gt;<\/code> should compile, and iterators into it should avoid copying the yielded strings when dereferenced.<\/p>\n<p>The design that the committee landed on for <code>std::generator<\/code> is that you can supply two template arguments to tune the reference and value types. If you only supply one, let\u2019s call it <code>Ref<\/code>, then the reference type is <code>Ref&amp;&amp;<\/code> and the value type is <code>std::remove_cvref_t&lt;Ref&gt;<\/code>. What this means in practice is that dereferencing the generator\u2019s iterators gives references, avoiding copies, and machinery that relies on <code>std::ranges::range_value_t<\/code> will act on values of the given type. Again, an example with code:<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">std::generator&lt;std::string&gt; gen() {\r\n\u00a0 \u00a0 std::string hello = \"hello\";\r\n\u00a0 \u00a0 co_yield hello; \/\/ 0 copies\r\n\u00a0  \u00a0co_yield \"Hello\"; \/\/ 1 copy (conversion from const char* to std::string)\r\n}\r\nint main() {\r\n\u00a0 \u00a0 for (auto&amp;&amp; s : gen()) {} \/\/ 0 copies\r\n    \/\/copies all strings (no dangling refs)\r\n\u00a0 \u00a0 auto vec = std::ranges::to&lt;std::vector&gt;(gen());\r\n}<\/code><\/pre>\n<p>This all acts as we expect. It compiles, we avoid all the copies we can (except for the string literal), and we don\u2019t avoid copies we shouldn\u2019t. If we want to avoid copying that string literal, we could choose to instead generate <code><a href=\"https:\/\/learn.microsoft.com\/cpp\/standard-library\/string-view\">std::string_view<\/a><\/code>s, but this has a problem:<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">std::generator&lt;std::string_view&gt; gen() {\r\n\u00a0 \u00a0 std::string hello = \"hello\";\r\n\u00a0 \u00a0 co_yield hello; \/\/ 0 copies of string data\r\n\u00a0 \u00a0 co_yield \"Hello\"; \/\/ 0 copies\r\n}\r\n\r\nint main() {\r\n\u00a0 \u00a0 for (auto s : gen()) {} \/\/ 0 copies\r\n    \/\/ uh oh, dangling references\r\n\u00a0 \u00a0 auto vec = std::ranges::to&lt;std::vector&gt;(gen());\r\n}<\/code><\/pre>\n<p>In this case, the value type of the generator is <code>std::string_view<\/code>, so <code><a href=\"https:\/\/en.cppreference.com\/w\/cpp\/ranges\/to\">std::ranges::to<\/a><\/code> copies everything into a <code>std::vector&lt;std::string_view&gt;<\/code>, which holds a bunch of dangling references. Not ideal.<\/p>\n<p>The solution is to manually specify the value type of the generator with the second template argument:<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">std::generator&lt;std::string_view, std::string&gt; gen() {\r\n\u00a0 \u00a0 std::string hello = \"hello\";\r\n\u00a0 \u00a0 co_yield hello; \/\/ 0 copies of string data\r\n\u00a0 \u00a0 co_yield \"Hello\"; \/\/ 0 copies\r\n}\r\n\r\nint main() {\r\n\u00a0 \u00a0 for (auto s : gen()) {} \/\/ 0 copies\r\n\u00a0 \u00a0 \/\/ copies all strings, no dangling ref\r\n\u00a0 \u00a0 auto vec = std::ranges::to&lt;std::vector&gt;(gen());\r\n}<\/code><\/pre>\n<p>This is the best of all worlds.<\/p>\n<p>The moral is: if the template parameter you\u2019re supplying to <code>std::generator<\/code> has reference semantics, think about manually supplying a reasonable value type to avoid dangling references.<\/p>\n<h3>Allocator Support<\/h3>\n<p>Coroutines require the compiler to generate code that tracks the state of the coroutine and its local variables. This is called a <em>coroutine frame<\/em> or <em>activation record<\/em>. In general, the compiler allocates the coroutine frame on the heap unless it can detect that it never outlives its caller, in which case it instead allocates the frame on the caller\u2019s stack. This optimization is called the <a href=\"https:\/\/wg21.link\/p0981\">coroutine Heap Allocation eLision Optimization<\/a> (HALO). By default, <code>std::generator<\/code> uses <code><a href=\"https:\/\/en.cppreference.com\/w\/cpp\/memory\/allocator\">std::allocator<\/a><\/code> for allocating its coroutine frame. In case you want more fine-grained control over where the frame for your <code>std::generator<\/code> is allocated, it supports supplying an allocator in a few different ways.<\/p>\n<p>First, you can add an allocator to the coroutine\u2019s parameter list:<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">template &lt;class Allocator&gt;\r\nstd::generator&lt;int&gt; f(std::allocator_arg_t, Allocator alloc);\r\n\r\n\/\/ Usage\r\nf(std::allocator_arg, MyAlloc{});<\/code><\/pre>\n<p>The first parameter must be <code>std::allocator_arg_t<\/code>, then the second parameter defines the allocator to use.<\/p>\n<p>You can also supply the allocator to use as the third template argument for <code>std::generator<\/code>:<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">std::generator&lt;int, void, StatelessAllocator&lt;int&gt;&gt; f();\r\n\r\n\/\/ Usage\r\nf();<\/code><\/pre>\n<p>Finally, you can combine these two methods if you have a stateful allocator that you want to specify the type of statically:<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">std::generator&lt;int, void, StatefulAllocator&lt;int&gt;&gt;\r\nf(std::allocator_arg_t, StatefulAllocator&lt;int&gt; alloc);\r\n\r\n\/\/ Usage\r\nf(std::allocator_arg, some_allocator); \/\/ must be convertible to StatefulAllocator&lt;int&gt;<\/code><\/pre>\n<h2>A Short Note on Performance<\/h2>\n<p>Current implementations of <code>std::generator<\/code> for both MSVC and libstdc++\/GCC introduce some overhead. In the above <code>xorshift<\/code> sample, the generator version shows around a 3x slowdown with MSVC and 2x slowdown with GCC. We hope to improve on this in future versions of the compiler by, for example, enabling HALO for this use case.<\/p>\n<h2>Send Us Your Feedback<\/h2>\n<p>We are very much interested in your feedback to continue to improve our standard library and compiler. The comments below are open. You can also share feedback through <a href=\"https:\/\/github.com\/microsoft\/STL\/issues\">GitHub Issues<\/a> for the standard library and\u00a0<a href=\"https:\/\/developercommunity.visualstudio.com\/cpp\">Visual Studio Developer Community<\/a> for the compiler. You can also reach us via email at\u00a0<a href=\"mailto:visualcpp@microsoft.com\">visualcpp@microsoft.com<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>std::generator is a C++23 feature that enables you to write concise, straightforward functions that generate sequences of values on-demand without manually managing state. It builds upon C++20\u2019s coroutines, providing some standard library support for this powerful, but complex, language feature. As of Visual Studio 2022 version 17.13, we ship an implementation of std::generator in our [&hellip;]<\/p>\n","protected":false},"author":706,"featured_media":35994,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1,297,512],"tags":[],"class_list":["post-35153","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cplusplus","category-coroutine","category-general-cpp-series"],"acf":[],"blog_post_summary":"<p>std::generator is a C++23 feature that enables you to write concise, straightforward functions that generate sequences of values on-demand without manually managing state. It builds upon C++20\u2019s coroutines, providing some standard library support for this powerful, but complex, language feature. As of Visual Studio 2022 version 17.13, we ship an implementation of std::generator in our [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/35153","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/users\/706"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/comments?post=35153"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/35153\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media\/35994"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media?parent=35153"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/categories?post=35153"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/tags?post=35153"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}