The reason for the question is that I've seen code like this:
auto fun(std::vector<Foo>&& v) {
std::vector<Bar> w;
for (auto&& e : v /* not an rvalue, but keep reading */) {
w.push_back(std::move(e));
}
// do stuff with w
}
which is marked as erroneous by static analisys tools, as the forwarding reference e
is being std::move
d instead of being std::forward
ed.
On the other hand, v
binds for sure to a prvalue or an xvalue (something the client knows to be or wants fun
to treat as a temporary), because its type is an rvalue reference. Yes, I see that the body of the function doesn't state in any way that v
cannot be used after the for
loop, but that would only lead me to think that I should change
for (auto&& e : v)
to for (auto&& e : std::move(v))
,auto&&
to E&&
, assuming something along the lines of using E = std::decay_t<decltype(v)>::value_type;
.As far as I've understood, the first point doesn't have the effect I would have expected. In fact, std::move
seems to have no effect as far as the for
is concerned. In turn, e
keeps being initialized from an lvalue (at least if the frequent case that operator[]
returns a reference for the type of v
), and the second point simply causes a compilation error.
As an additional reference, the note ¹
from this answer reads (with reference to range-for
loops)
You cannot detect if you are iterating over a temporary (or other rvalue)
which seems to confirm that I just can't do that.
But looking at how a range-for
loop is desugared, what would be wrong in changing range-declaration = *__begin;
to range-declaration = std::move(*__begin);
when range-expression
is an rvalue?
the container is rvalue doesn't means what it contains is
// note: in real code this could be T&& and have no idea it's a std::span
void foo(std::span<X>&& s){
// explicitly std::move should be required
// because the code may want to use `s` later
for(auto&& v : std::move(s)){
// if the for loop forward the value category of <range-expression> to items
// then decltype(v) is X&&, and this wrongly move-out the data
X t = std::forward<decltype(v)>(v);
}
}
int main(){
X x[2];
foo(x);
}
the decision should be done by
std::move_iterator
*std::forward_like
for(auto&& v : forward_content<...>(s))
* the container/adaptor should always return move_iterator
, overload for rvalue begin
/end
would not work in current standard, the normal one is always called. Plus no standard container support them either.