I wrote the following function to generate pairs from input ranges.
template <typename Range>
requires std::ranges::borrowed_range<Range>
auto pair(Range && r)
{
// Creating a range of iterators to the original range
auto iterator_range = std::views::iota(
std::ranges::begin(r),
std::ranges::end(r));
return iterator_range
| std::views::drop(1)
| std::views::transform([](auto const & it){
using T = std::ranges::range_reference_t<Range>;
return std::pair<T,T>(*std::prev(it), *it);
});
}
It specifically has the constraint borrowed_range against the input so as not to accidentally take non borrowed ranges and get dangling iterators. This works perfectly for an input of std::vector.
auto number_range = std::vector{0,1,2,3,4,5,6,7,8,9};
for(auto [a, b] : pair(number_range))
std::cout << a << ", " << b << std::endl;
A reference to a vector is a borrowed range. However I thought I'd test it with an iota range as an input. It too is a borrowed range and because the types stored in the pair are set using using T = std::ranges::range_reference_t<Range>;
I figured it would just work. However I was surprised when the below is compiled into an infinite loop.
auto number_range = std::views::iota(0, 10);
for(auto [a, b] : pair(number_range))
std::cout << a << ", " << b << std::endl;
What's going on here? The gcc version is linked below and crashes
https://godbolt.org/z/q1EEaj6bW
Is there some undefined behaviour in my code? The clang version is linked below and runs as expected.
https://godbolt.org/z/c3fMqej31
Expected Results
std::vector
0, 1
1, 2
2, 3
3, 4
4, 5
5, 6
6, 7
7, 8
8, 9
std::views::iota
0, 1
1, 2
2, 3
3, 4
4, 5
5, 6
6, 7
7, 8
8, 9
Your code is UB.
std::prev
only works with Cpp17BidirectionalIterator, and although the iterator satisfies bidirectional_iterator
in the case of iota_view
, it does not meet the requirements of Cpp17BidirectionalIterator as its reference is pr-value, so it is just a C++17 input iterator. Applying std::prev
to a C++17 input iterator is undefined behavior.
Using ranges::prev
works as expected for both compiler.