Is it possible to have a range of generators and if yes, is it possible to join them ?
#include <algorithm>
#include <ranges>
#include <vector>
#include <cstdio>
#include <generator>
#include <iostream>
using namespace std;
template<typename T>
struct Tree
{
T value;
Tree *left{}, *right{};
std::generator<const T&> traverse_inorder() const
{
if (left)
co_yield std::ranges::elements_of(left->traverse_inorder());
co_yield value;
if (right)
co_yield std::ranges::elements_of(right->traverse_inorder());
}
};
int main()
{
Tree<char> tree1[]
{
{'D', tree1 + 1, tree1 + 2},
// │
// ┌───────────────┴────────────────┐
// │ │
{'B', tree1 + 3, tree1 + 4}, {'F', tree1 + 5, tree1 + 6},
// │ │
// ┌─────────┴─────────────┐ ┌───────────┴─────────────┐
// │ │ │ │
{'A'}, {'C'}, {'E'}, {'G'}
};
generator<const char&> gen = tree1->traverse_inorder();
for (char x : gen)
std::cout << x << ' ';
std::cout << '\n';
generator<const char&> gen3[3]={gen,gen,gen};
for (char x : gen3 | views::join)
std::cout << x << ' ';
}
prog.cc: In function 'int main()':
prog.cc:47:48: error: use of deleted function 'std::generator<_Ref, _Val, _Alloc>::generator(const std::generator<_Ref, _Val, _Alloc>&) [with _Ref = const char&; _Val = void; _Alloc = void]'
47 | generator<const char&> gen3[3]={gen,gen,gen};
| ^
In file included from prog.cc:5:
/opt/wandbox/gcc-head/include/c++/16.0.0/generator:712:7: note: declared here
712 | generator(const generator&) = delete;
| ^~~~~~~~~
prog.cc:47:48: note: use '-fdiagnostics-all-candidates' to display considered candidates
47 | generator<const char&> gen3[3]={gen,gen,gen};
| ^
prog.cc:47:48: error: use of deleted function 'std::generator<_Ref, _Val, _Alloc>::generator(const std::generator<_Ref, _Val, _Alloc>&) [with _Ref = const char&; _Val = void; _Alloc = void]'
/opt/wandbox/gcc-head/include/c++/16.0.0/generator:712:7: note: declared here
712 | generator(const generator&) = delete;
| ^~~~~~~~~
prog.cc:47:48: note: use '-fdiagnostics-all-candidates' to display considered candidates
47 | generator<const char&> gen3[3]={gen,gen,gen};
| ^
prog.cc:47:48: error: use of deleted function 'std::generator<_Ref, _Val, _Alloc>::generator(const std::generator<_Ref, _Val, _Alloc>&) [with _Ref = const char&; _Val = void; _Alloc = void]'
/opt/wandbox/gcc-head/include/c++/16.0.0/generator:712:7: note: declared here
712 | generator(const generator&) = delete;
| ^~~~~~~~~
prog.cc:47:48: note: use '-fdiagnostics-all-candidates' to display considered candidates
47 | generator<const char&> gen3[3]={gen,gen,gen};
| ^
Can ranges of std::generator be joined? Definitely. Here's how your example can be easily fixed
generator<const char&> gen3[3]={
tree1->traverse_inorder(),
tree1->traverse_inorder(),
tree1->traverse_inorder()
};
for (char x : gen3 | views::join)
std::cout << x << ' ';
A std::generator isn't copyable, the coroutine state can only be owned by a single std::generator object. It's a sane default. So the easy fix is just create three distinct coroutines from your tree.
If you have pre-existing generators you can wrap them in std::ranges::ref_view
std::ranges::ref_view<generator<const char&>> gen3[3]={gen1, gen2, gen3};
That is of course assuming you own them, they aren't past their final suspend point, and they are all distinct object. That is also why you can't fix your own attempt by trying this. The reference semantics will make it refer to the same generator. I.e. this
std::ranges::ref_view<generator<const char&>> gen3[3]={gen, gen, gen};
will fail, since it will attempt to resume gen after it's fully exhausted the first time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With