Ensuring the lifetime of the container outlives the std::ranges::subrange
is crucial since std::ranges::subrange
does not own the data it views. Here are some best practices and techniques to manage lifetimes effectively.
When working with stack-allocated containers, ensure that the container's scope covers the entire usage of the subrange.
Define the container before the subrange and keep both within the same scope. For example:
#include <iostream>
#include <ranges>
#include <vector>
int main() {
std::vector<int> Nums{1, 2, 3, 4, 5};
{
std::ranges::subrange view{Nums};
for (int n : view) {
std::cout << n << ", ";
}
} // view goes out of scope after Nums
}
1, 2, 3, 4, 5,
When using heap-allocated containers, manage the lifetime using smart pointers such as std::shared_ptr
to ensure the container remains valid for the duration of the subrange. For example:
#include <iostream>
#include <memory>
#include <ranges>
#include <vector>
int main() {
auto Nums = std::make_shared<std::vector<int>>(
std::initializer_list<int>{1, 2, 3, 4, 5});
std::ranges::subrange view{
Nums->begin(), Nums->end()};
for (int n : view) {
std::cout << n << ", ";
}
}
1, 2, 3, 4, 5,
When passing containers to functions, use references or smart pointers to ensure the lifetime is managed correctly. Avoid returning subranges that reference local variables. For example:
#include <iostream>
#include <memory>
#include <ranges>
#include <vector>
std::ranges::subrange<std::vector<int>::iterator>
get_subrange(std::shared_ptr<
std::vector<int>> nums) {
return std::ranges::subrange{
nums->begin(), nums->end()};
}
int main() {
auto Nums = std::make_shared<std::vector<int>>(
std::initializer_list<int>{1, 2, 3, 4, 5});
auto view = get_subrange(Nums);
for (int n : view) {
std::cout << n << ", ";
}
}
1, 2, 3, 4, 5,
Be cautious of creating subranges from temporary containers, as this can lead to dangling references. The following program is problematic, as it creates a dangling pointer:
#include <iostream>
#include <ranges>
#include <vector>
std::ranges::subrange<std::vector<int>::iterator>
bad_example() {
std::vector<int> temp{1, 2, 3, 4, 5};
// This is problematic as temp will be
// deallocated when the function returns
return std::ranges::subrange{
temp.begin(), temp.end()
};
}
int main() {
auto view = bad_example();
for (int n : view) {
std::cout << n << ", ";
}
}
error: reference to local variable 'temp' returned
By following these practices, you can ensure that the lifetime of the container outlives the std::ranges::subrange
, preventing undefined behavior and potential crashes.
Answers to questions are automatically generated and may not have been reviewed.
std::ranges::subrange
This lesson introduces std::ranges::subrange, allowing us to create non-owning ranges that view some underlying container