Assume that I have a function template that can accept a range of some type T
. I want to check whether it's safe to move from that range.
For example, if a function accepts an rvalue reference to a certain value, it is safe to move from it. If, for example, that function may accept both rvalue and lvalue references, we can use std::forward
to conditionally move it if necessary.
Now I want to achieve the same thing, but for ranges. Initially I tried using the following concept:
template <typename Range>
concept movable_source = std::ranges::range<Range> &&
!std::ranges::borrowed_range<Range> &&
(!std::is_lvalue_reference_v<Range>);
and it worked okay. It reported true
for std::vector<T>&&
, false
for std::vector<T>&
and true
again for std::vector<T>
.
Usage:
template <typename T>
auto print_movability(T&& t) -> void {
std::cout << movable_source<T> << ' ';
}
auto main() -> int {
auto vec = std::vector<int>();
print_movability(vec);
print_movability(std::move(vec));
print_movability(std::vector<int>());
}
This correctly prints 0 1 1
.
However, the following snippet breaks this logic:
print_movability(vec | std::views::transform([](int& e) -> int& { return e; }));
This reports 1
since the resulting view is neither a borrowed range nor is it an lvalue reference.
I could of course simply add another check to make sure that the type isn't a view, but would that be enough? What would be the correct way to test whether a type is a range whose elements we can move?