In the code below std::ranges::minmax_element
return iterators on a temporary view.
In the next line the iterator is dereferenced.
Copilot claims that the code is safe because the lifetime of the temporary range is the current scope. I have hard time to believe that.
Can someone tell if the code is safe ?
And if it is not safe how can we prevent someone to write it ?
#include <iostream>
#include <map>
#include <algorithm>
#include <ranges>
int main() {
std::map<int,int> numbers = {{1,2}, {2,4}, {8,-1}};
auto result = std::ranges::minmax_element(numbers | std::views::values);
std::cout << "Min: " << *result.min << std::endl;
}
std::ranges::values_view<R>
(which is an alias for std::ranges::elements_view<R, 1>
) has an iterator type that only has one member, an iterator of type std::ranges::iterator_t<R>
. That is, an iterator of the 'base' range.
The key point is:
values_view
that it came from, so there is no chance it accesses stuff invalidly.std::ranges::minmax_element
returns a pair of iterators to a borrowed_range
. Standard library range types (and your own range types can via std::ranges::enable_borrowed_range
) tell this that iterators will never dangle, even if given an rvalue.
If it weren't a borrowed range, it would return a std::ranges::dangling
to prevent access:
#include <algorithm>
#include <iostream>
#include <vector>
int main() {
auto result = std::ranges::minmax_element(std::vector<int>{1, 2, 3});
std::cout << "Min: " << *result.min << '\n';
// Error: result.min is a std::ranges::dangling and doesn't have operator*
}
Because the vector is out of destroyed after result
is initialised, the returned iterators would have been dangling anyways.
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