The code below, when compiled under g -11.3 using --std=c 20 -D_GLIBCXX_DEBUG
and executed, produces a bizarre runtime error about iterators. I'm not exactly sure what it means but I suspect it has something to do with the vector range
going out of scope when test()
returns: range
doesn't get moved or copied, rather whatever it is the pipe operator returns just retains a reference to range
.
#include <ranges>
#include <unordered_set>
#include <vector>
auto to_unordered_set(auto && range) {
using r_type = std::ranges::range_value_t<decltype(range)>;
auto common = range | std::views::common;
return std::unordered_set<r_type>(std::ranges::begin(common), std::ranges::end(common));
}
auto test() {
std::vector<int> range {1,2,3,4,5};
return range | std::ranges::views::transform([](auto x) { return x%2; });
}
int main() {
auto y = to_unordered_set(test());
return 0;
}
/*
/opt/compiler-explorer/gcc-11.3.0/include/c /11.3.0/debug/safe_iterator.h:195:
In function:
__gnu_debug::_Safe_iterator<_Iterator, _Sequence,
_Category>::_Safe_iterator(__gnu_debug::_Safe_iterator<_Iterator,
_Sequence, _Category>&&) [with _Iterator =
__gnu_cxx::__normal_iterator<int*, std::__cxx1998::vector<int,
std::allocator<int> > >; _Sequence = std::__debug::vector<int>;
_Category = std::forward_iterator_tag]
Error: attempt to copy-construct an iterator from a singular iterator.
Objects involved in the operation:
iterator "this" @ 0x0x7ffea2b7a8c0 {
type = __gnu_cxx::__normal_iterator<int*, std::__cxx1998::vector<int, std::allocator<int> > > (mutable iterator);
state = singular;
}
iterator "other" @ 0x0x7ffea2b7a820 {
type = __gnu_cxx::__normal_iterator<int*, std::__cxx1998::vector<int, std::allocator<int> > > (mutable iterator);
state = singular;
references sequence with type 'std::__debug::vector<int, std::allocator<int> >' @ 0x0x7ffea2b7a8b0
}
*/
Is there anyway to get something like this to work? I basically want to transform / filter / join / etc.. a range and return it (a copy/move of the entire thing gets returned, both the range and whatever modifications get applied to it).
CodePudding user response:
First, since the std::vector
itself is common_range
, transform_view
will also be common_range
, so using views::common
here is redundant.
Second and more important, range
is a local variable, so it will be destroyed once it leaves test()
, which makes test()
return a transform_view
that holds a dangling pointer.
Is there anyway to get something like this to work?
Thanks to P2415, you can directly return a transform_view
applied to a rvalue vector
, which will construct an owning_view
that transfers ownership of the original vector
's contents to itself, which will no longer cause dangling.
#include <ranges>
#include <unordered_set>
#include <vector>
auto to_unordered_set(auto && range) {
using r_type = std::ranges::range_value_t<decltype(range)>;
return std::unordered_set<r_type>(
std::ranges::begin(range), std::ranges::end(range));
}
auto test() {
std::vector<int> range {1,2,3,4,5};
return std::move(range) |
std::ranges::views::transform([](auto x) { return x%2; });
}
int main() {
auto y = to_unordered_set(test());
}