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.