Basically I am trying to get around the fact that the following does not work with range-v3 ranges: (this is a toy example but illustrates the problem.)
namespace rv = ranges::views;
auto bad_foo() {
std::vector<int> local_vector = { 23, 45, 182, 3, 5, 16, 1 };
return local_vector |
rv::transform([](int n) {return 2 * n; });
}
int main() {
for (int n :bad_foo()) {
std::cout << n << " ";
}
std::cout << "\n";
return 0;
}
The above doesn't work because we are returning a view that references data that will go out of scope when bad_foo returns.
What I want to do is store the vector in a shared pointer in such a way that the range view will keep the shared pointer alive. Note that the obvious version of this idea does not work i.e.
auto still_bad_foo() {
auto shared_vector = std::make_shared<std::vector<int>>(
std::initializer_list{ 23, 45, 182, 3, 5, 16, 1 }
);
return *shared_vector |
rv::transform([](int n) {return 2 * n; });
}
still fails because shared_vector is not actually getting captured by the range view. What we want to do is force the range view to own the shared pointer.
The following seems to work to me.
auto good_foo() {
auto shared_vector = std::make_shared<std::vector<int>>(
std::initializer_list{ 23, 45, 182, 3, 5, 16, 1 }
);
return rv::single(shared_vector) |
rv::transform([](auto ptr) { return rv::all(*ptr); }) |
rv::join |
rv::transform([](int n) {return 2 * n; });
}
We use single to turn the shared pointer into a single item range view, turn the single item into a range of ints by dereferencing the pointer in transform and wrapping the vector in a range with all, yielding a composition of range views, which we then flatten via join.
My question is is the above really safe and if so is there a less verbose version of the same idea?
Edit: updated the question to just be about range-v3 ranges as @康桓瑋 has alerted me that this issue is not a problem with C++20 ranges as long as it is not a requirement for the owning range views to be copyable.