I am currently reading about the range
library. My question is about resource ownerships when using views
, and how to use these safely.
Let's consider the following code, which print the even year in reverse order:
#include <algorithm>
#include <iostream>
auto print = [](auto elem) -> void { std::cout << " " << elem <<"," << std::endl; };
auto isEven = [](size_t year) -> bool {return !(year % 2);};
int main() {
auto years = std::vector<size_t>{
1998, 2003, 2011, 2014,
2017, 2020, 2023};
auto view = years |
std::ranges::views::filter(isEven) |
std::ranges::views::reverse ;
std::ranges::for_each(view, print);
}
Now, consider the following refactoring, where the view is created with a function:
#...
auto createViewOnEvenYears() {
auto years = std::vector<size_t>{
1998, 2003, 2011, 2014,
2017, 2020, 2023};
auto view = years |
std::ranges::views::filter(isEven) |
std::ranges::views::reverse ;
return view;
}
int main() {
auto view = createViewOnEvenYears();
std::cout << "Printing after vector destruction: " << std::endl;
std::ranges::for_each(view, print);
}
As the vector has been destroyed when iterating on the view, this lead to a SEGFAULT.
My questions are:
- are
view
compatible with the Resource Acquisition Is Initialization principle? - Should my function return the range adaptor to prevent this (as proposed below)?
- More generally, what design pattern(s) should I use to avoid
dangling views
?
std::vector<size_t> createYears() {
return std::vector<size_t>{
1998, 2003, 2011, 2014,
2017, 2020, 2023};
}
auto createEvenReverseRangeAdaptor() {
return
std::ranges::views::filter(isEven) |
std::ranges::views::reverse ;
}
int main() {
auto years = createYears();
std::ranges::for_each(years | createEvenReverseRangeAdaptor(), print);
}