This question already has answers here:
Why is function parameter always an lvalue?
(1 answer)
C++11: Why rvalue reference parameter implicitly converted to lvalue
(2 answers)
r-value parameters in a function
(3 answers)
Is the r-value parameter really an l-value parameter inside the function scope?
(1 answer)
Closed 4 months ago.
I pass rvalue std::move(x) to testForward(T&& v), but it calls print(T& t) inside.
It seems that the rvalue v has changed to an lvalue before it calls print(). I do not know why this happened. Can anyone explain it?
#include<iostream>
using namespace std;
template<typename T>
void print(T& t) {
std::cout << "Lvalue ref" << std::endl;
}
template<typename T>
void print(T&& t) {
std::cout << "Rvalue ref" << std::endl;
}
template<typename T>
void testForward(T&& v) {
print(v); // call print(T& t);
}
int main(int argc, char* argv[])
{
int x = 1;
testForward(std::move(x)); // output: Lvalue ref
}
The value category of the expression v is an lvalue, because:
... Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression
If you want to forward a forwarding reference as its original category, use std::forward, ie,
template<typename T>
void testForward(T&& v) {
print(std::forward<T>(v));
}
Related
This question already has answers here:
Why are string literals l-value while all other literals are r-value?
(4 answers)
C++ specialized template function receiving literal string
(1 answer)
Closed 7 months ago.
When trying to write a function that behaves differently for lvalue references and rvalue references I noticed, that C strings seem to always be considered as lvalue references.
#include <iostream>
template <typename T>
struct Holder {
T value;
};
template <typename T>
Holder<T> some_function(T&& t) {
std::cout<< "overload T&&, "
<< typeid(T).name()
<< std::endl;
return {std::move(t)};
}
template <typename T>
Holder<T*> some_function(T& t) {
std::cout<<"overload T&, "
<< typeid(T).name()
<< std::endl;
return {&t};
}
Here, the returned Holder holds a pointer to the argument if some_function is passed an lvalue reference and it moves the argument otherwise.
When calling some_function on std::string it behaves as expected. But when I pass const char* strings to it, it always consideres the argument to be an lvalue-reference:
int main()
{
std::string x = "lvalue string";
auto holder_string_lvalue = some_function(x);
auto holder_string_rvalue = some_function(std::string("rvalue string"));
const char* y = "lvalue cstring";
auto holder_cstring_lvalue = some_function(y);
auto holder_cstring_rvalue = some_function("rvalue cstring");
return 0;
}
Output:
overload T&, NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
overload T&&, NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
overload T&, PKc
overload T&, A15_c
I don't understand the fourth line in this. Also, the code should now have a Holder with an invalid pointer holder_cstring_rvalue, does it not?
How would I make sure Holder never points to a temporary object for arbitrary T, including c strings?
Compiler explorer link: https://godbolt.org/z/EMfETYc7e
I was testing code out and got stuck on this.
Here's my code:
#include <iostream>
template<typename T>
void check(T&& other) {
std::cout << "Rvalue" << std::endl;
}
template<typename T>
void check(T& other) {
std::cout << "Lvalue" << std::endl;
}
template<typename T>
void call(T other) {
check(std::forward<T>(other));
}
int main() {
std::string t = "Cool";
call(t);
}
Output:
RValue
Why is the output of this "RValue"? I did pass a LValue and when it forwarded, didn't it forward as a LValue? Why did it call the RValue function of check?
To use std::forward properly, your argument type should be T &&, not T. Fix it like this:
template<typename T>
void call(T&& other) {
check(std::forward<T>(other));
}
Then we get the expected result.
Online demo
Reference for std::forward
When t is a forwarding reference (a function argument that is declared
as an rvalue reference to a cv-unqualified function template
parameter), this overload forwards the argument to another function
with the value category it had when passed to the calling function.
This question already has an answer here:
Why does a templated rvalue reference accept lvalues?
(1 answer)
Closed 4 years ago.
This program:
using namespace std;
#include <iostream>
#include <memory>
struct Dog
{
int legs;
} adog;
Dog gimmeadog() { return adog; }
void walk(Dog && d) { cout << "Nonconst right dog walk\n"; }
//template<class T> void walk(T && d) { d.legs=3; cout << "Nonconst right something walk\n"; }
int main() {
Dog mydog = gimmeadog();
walk(mydog);
return 0;
}
correctly fails to compile on gcc because:
error: cannot bind rvalue reference of type ‘Dog&&’ to lvalue of type ‘Dog’
walk(mydog);
but if you un-comment the template it happily binds and prints "Nonconst right something walk".
Why? What type is T taking when it works? Is this not defeating the object of rvalue references?
When T is a template argument T&& is a forwarding reference, not a r-value one. T gets deduced as T for r-values and T& for l-values. Such a reference binds to anything.
template<class T>
void walk(T && d)
in this case T&& is forwarding reference, when you pass Lvalue (mydog object) T is deduced to be T&, so signature of walk looks like
void walk(Dog& );
then you can bind mydog to Lvalue reference.
This question already has answers here:
What are the main purposes of std::forward and which problems does it solve?
(7 answers)
Closed 6 years ago.
In a function template like this
template <typename T>
void foo(T&& x) {
bar(std::forward<T>(x));
}
Isn't x an rvalue reference inside foo, if foo is called with an rvalue reference? If foo is called with an lvalue reference, the cast isn't necessary anyway, because x will also be an lvalue reference inside of foo. Also T will be deduced to the lvalue reference type, and so std::forward<T> won't change the type of x.
I conducted a test using boost::typeindex and I get exactly the same types with and without std::forward<T>.
#include <iostream>
#include <utility>
#include <boost/type_index.hpp>
using std::cout;
using std::endl;
template <typename T> struct __ { };
template <typename T> struct prt_type { };
template <typename T>
std::ostream& operator<<(std::ostream& os, prt_type<T>) {
os << "\033[1;35m" << boost::typeindex::type_id<T>().pretty_name()
<< "\033[0m";
return os;
}
template <typename T>
void foo(T&& x) {
cout << prt_type<__<T>>{} << endl;
cout << prt_type<__<decltype(x)>>{} << endl;
cout << prt_type<__<decltype(std::forward<T>(x))>>{} << endl;
cout << endl;
}
int main(int argc, char* argv[])
{
foo(1);
int i = 2;
foo (i);
const int j = 3;
foo(j);
foo(std::move(i));
return 0;
}
The output of g++ -Wall test.cc && ./a.out with gcc 6.2.0 and boost 1.62.0 is
__<int>
__<int&&>
__<int&&>
__<int&>
__<int&>
__<int&>
__<int const&>
__<int const&>
__<int const&>
__<int>
__<int&&>
__<int&&>
Edit: I found this answer: https://stackoverflow.com/a/27409428/2640636 Apparently,
as soon as you give a name to the parameter it is an lvalue.
My question is then, why was this behavior chosen over keeping rvalue references as rvalues even when they are given names? It seems to me that the whole forwarding ordeal could be circumvented that way.
Edit2: I'm not asking about what std::forward does. I'm asking about why it's needed.
Isn't x an rvalue reference inside foo ?
No, x is a lvalue inside foo (it has a name and an address) of type rvalue reference. Combine that with reference collapsing rules and template type deduction rules and you'll see that you need std::forward to get the right reference type.
Basically, if what you pass to as x is a lvalue, say an int, then T is deduced as int&. Then int && & becomes int& (due to reference collapsing rules), i.e. a lvalue ref.
On the other hand, if you pass a rvalue, say 42, then T is deduced as int, so at the end you have an int&& as the type of x, i.e. a rvalue. Basically that's what std::forward does: casts to T&& the result, like a
static_cast<T&&>(x)
which becomes either T&& or T& due reference collapsing rules.
Its usefulness becomes obvious in generic code, where you may not know in advance whether you'll get a rvalue or lvalue. If you don't invoke std::forward and only do f(x), then x will always be a lvalue, so you'll be losing move semantics when needed and may end up with un-necessary copies etc.
Simple example where you can see the difference:
#include <iostream>
struct X
{
X() = default;
X(X&&) {std::cout << "Moving...\n";};
X(const X&) {std::cout << "Copying...\n";}
};
template <typename T>
void f1(T&& x)
{
g(std::forward<T>(x));
}
template <typename T>
void f2(T&& x)
{
g(x);
}
template <typename T>
void g(T x)
{ }
int main()
{
X x;
std::cout << "with std::forward\n";
f1(X{}); // moving
std::cout << "without std::forward\n";
f2(X{}); // copying
}
Live on Coliru
You really don't want your parameters to be automatically moved to the functions called. Consider this function:
template <typename T>
void foo(T&& x) {
bar(x);
baz(x);
global::y = std::forward<T>(x);
}
Now you really don't want an automatic move to bar and an empty parameter to baz.
The current rules of requiring you to specify if and when to move or forward a parameter are not accidental.
I get exactly the same types with and without std::forward<T>
...no? Your own output proves you wrong:
__<int> // T
__<int&&> // decltype(x)
__<int&&> // std::forward<T>(x)
Without using std::forward<T> or decltype(x) you will get int instead of int&&. This may inadvertently fail to "propagate the rvalueness" of x - consider this example:
void foo(int&) { cout << "int&\n"; }
void foo(int&&) { cout << "int&&\n"; }
template <typename T>
void without_forward(T&& x)
{
foo(x);
// ^
// `x` is an lvalue!
}
template <typename T>
void with_forward(T&& x)
{
// `std::forward` casts `x` to `int&&`.
// vvvvvvvvvvvvvvvvvv
foo(std::forward<T>(x));
// ^
// `x` is an lvalue!
}
template <typename T>
void with_decltype_cast(T&& x)
{
// `decltype(x)` is `int&&`. `x` is casted to `int&&`.
// vvvvvvvvvvv
foo(decltype(x)(x));
// ^
// `x` is an lvalue!
}
int main()
{
without_forward(1); // prints "int&"
with_forward(1); // prints "int&&"
with_decltype_cast(1); // prints "int&&"
}
wandbox example
x being an r-value is NOT the same thing as x having an r-value-reference type.
R-value is a property of an expression, whereas r-value-reference is a property of its type.
If you actually try to pass a variable that is an r-value reference to a function, it is treated like an l-value. The decltype is misleading you. Try it and see:
#include <iostream>
#include <typeinfo>
using namespace std;
template<class T> struct wrap { };
template<class T>
void bar(T &&value) { std::cout << " vs. " << typeid(wrap<T>).name() << std::endl; }
template<class T>
void foo(T &&value) { std::cout << typeid(wrap<T>).name(); return bar(value); }
int main()
{
int i = 1;
foo(static_cast<int &>(i));
foo(static_cast<int const &>(i));
foo(static_cast<int &&>(i));
foo(static_cast<int const &&>(i));
}
Output:
4wrapIRiE vs. 4wrapIRiE
4wrapIRKiE vs. 4wrapIRKiE
4wrapIiE vs. 4wrapIRiE (these should match!)
4wrapIKiE vs. 4wrapIRKiE (these should match!)
pass() reference argument and pass it to reference, however a rvalue argument actually called the reference(int&) instead of reference(int &&), here is my code snippet:
#include <iostream>
#include <utility>
void reference(int& v) {
std::cout << "lvalue" << std::endl;
}
void reference(int&& v) {
std::cout << "rvalue" << std::endl;
}
template <typename T>
void pass(T&& v) {
reference(v);
}
int main() {
std::cout << "rvalue pass:";
pass(1);
std::cout << "lvalue pass:";
int p = 1;
pass(p);
return 0;
}
the output is:
rvalue pass:lvalue
lvalue pass:lvalue
For p it is easy to understand according to reference collapsing rule, but why the template function pass v to reference() as lvalue?
template <typename T>
void pass(T&& v) {
reference(v);
}
You are using a Forwarding reference here quite alright, but the fact that there is now a name v, it's considered an lvalue to an rvalue reference.
Simply put, anything that has a name is an lvalue. This is why Perfect Forwarding is needed, to get full semantics, use std::forward
template <typename T>
void pass(T&& v) {
reference(std::forward<T>(v));
}
What std::forward<T> does is simply to do something like this
template <typename T>
void pass(T&& v) {
reference(static_cast<T&&>(v));
}
See this;
Why the template function pass v to reference() as lvalue?
That's because v is an lvalue. Wait, what? v is an rvalue reference. The important thing is that it is a reference, and thus an lvalue. It doesn't matter that it only binds to rvalues.
If you want to keep the value category, you will have to do perfect forwarding. Perfect forwarding means that if you pass an rvalue (like in your case), the function will be called with an rvalue (instead of an lvalue):
template <typename T>
void pass(T&& v) {
reference(std::forward<T>(v)); //forward 'v' to 'reference'
}