So I wanted to practice the usage of std::forward
and created a Test
class with 2 constructors. 1 with T&
and the other with T&&
as overload. T&
prints lvalue, and T&&
prints rvalue so I know which one of the constructors is being used. I create 2 instances of class on stack and to my surprise both of which are using the T&&
overload.
#include <iostream>
#include <type_traits>
#include <utility>
template <class T> auto forward(T &&t) {
if constexpr (std::is_lvalue_reference<T>::value) {
return t;
}
return std::move(t);
}
template <class T> class Test {
public:
Test(T &) { std::cout << "lvalue" << std::endl; };
Test(T &&) { std::cout << "rvalue" << std::endl; };
};
int main() {
int x = 5;
Test<int> a(forward(3));
Test<int> b(forward(x));
return 0;
}
I tried using the original std::forward
function and implementing it but both times it printed rvalue x2. What am I doing wrong?
lvalue references are marked with one ampersand (&). And an rvalue reference is a reference that binds to an rvalue. rvalue references are marked with two ampersands (&&). Note that there is one exception: there can be lvalue const reference binding to an rvalue.
An rvalue reference is formed by placing an && after some type. An rvalue reference behaves just like an lvalue reference except that it can bind to a temporary (an rvalue), whereas you can not bind a (non const) lvalue reference to an rvalue.
An lvalue (locator value) represents an object that occupies some identifiable location in memory (i.e. has an address). rvalues are defined by exclusion. Every expression is either an lvalue or an rvalue, so, an rvalue is an expression that does not represent an object occupying some identifiable location in memory.
An rvalue is an expression that is not an lvalue. Examples of rvalues include literals, the results of most operators, and function calls that return nonreferences. An rvalue does not necessarily have any storage associated with it.
Your problem stems from the return type of forward
. You use auto
as the return type which will not deduce a reference for you. That means when you do return, no matter which branch it returns from, you return by value which means you have a prvalue.
What you need is decltype(auto)
so you return an rvalue or lvalue reference, depending on the return statement. Using
template <class T> decltype(auto) forward(T &&t) {
if constexpr (std::is_lvalue_reference<T>::value)
return t;
else
return std::move(t);
}
gives you the output:
rvalue
lvalue
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With