Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

lifetime of temporary ranges in c++

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; 
}
like image 874
Joseph Perez Avatar asked Sep 02 '25 09:09

Joseph Perez


1 Answers

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:

  • There is no reference to the now-out-of-lifetime 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.

like image 189
Artyer Avatar answered Sep 04 '25 23:09

Artyer