I am probably missing something basic here, but why don't I get a lvalue from std:forward
bellow?
#include <iostream>
#include <utility>
template <typename T>
void f(const T& lhs) {
std::cout << "lvalue" << "\n";
}
template <typename T>
void f(const T&& rhs) { // const, so as not to have a universal reference
std::cout << "rvalue" << "\n";
}
int main() {
auto a = 3;
f(a); // lvalue
f(3); // rvalue
f(std::forward<int>(a)); // rvalue ???
}
this compiles to
lvalue
rvalue
rvalue
I would expect the last result to be a lvalue.
CodePudding user response:
std::forward
is a conditional std::move
. Using it only makes sense inside of a template, where you choose whether to move or not depending on a template argument.
Like this:
template <typename T>
void foo(T &&value)
{
f(std::forward<T>(value));
}
Here, T &&value
is called a forwarding reference (as long T
is deduced by the compiler, rather than specified manually), i.e. can accept both lvalues and rvalues.
If you pass an rvalue to this function, T
is deduced to a non-reference, so T &&value
remains an rvalue reference.
If you pass an lvalue, T
is deduced to an lvalue reference, and T &&value
becomes an lvalue reference (see reference collapsing rules).
To work with forwarding references, std::forward
acts as an std::move
if its template argument is not a reference, and does nothing if its template argument is an lvalue reference.
Since you passed a non-reference to it, you got a move, but again, it's not its intended use case.
CodePudding user response:
@HolyBlackCat's answer made me realise the problem and the solution. I'm not returning a reference from std:forward
but an int
so that's indeed a rvalue (since it doesn't have an address). With std::forward<int&>
it's indeed correctly forwarding the lvalue reference. So this:
f(a); // lvalue
f(3); // rvalue
f(std::move(a)); // rvalue
f(std::forward<int>(a)); // rvalue !!!
f(std::forward<int&>(a)); // lvalue
correctly returns
lvalue
rvalue
rvalue
rvalue
lvalue