Question

I have the following code directly from : http://www.justsoftwaresolutions.co.uk/cplusplus/rvalue_references_and_perfect_forwarding.html

compiled at g++ 4.8.1 as : g++ -std=c++11 testforward.cpp -o testforward.exe

#include <cstdlib>
#include <vector> 
#include <string> 
#include <iostream>   
#include <algorithm> 

class X
{
    std::vector<double> data;
public:
    X():
        data(100000) // lots of data
    {}
    X(X const& other): // copy constructor
        data(other.data)   // duplicate all that data
    {}
    X(X&& other):  // move constructor
        data(std::move(other.data)) // move the data: no copies
    {}

    X& operator=(X const& other) // copy-assignment
    {
        data=other.data; // copy all the data
        return *this;
    }

    X& operator=(X && other) // move-assignment
    {
        data=std::move(other.data); // move the data: no copies
        return *this;
    }
};



void g(X&& t)
{
    std::cout << "t in g is rvalue" << std::endl ;
}
void g(X& t)
{
    std::cout << "t in g is lvalue" << std::endl ;
}

template<typename T>
void f(T&&t)
{
    g(std::forward<T>(t)) ;
}

void h(X &&t)
{
    g(t) ;
}


int main()
{
    X x;
    f(x);   // 1
    f(X()); // 2
    //h(x);  //compile error 
    h(X()); // 3
}

According to the author describe below :

When you combine rvalue references with function templates you get an interesting interaction: if the type of a function parameter is an rvalue reference to a template type parameter then the type parameter is deduce to be an lvalue reference if an lvalue is passed, and a plain type otherwise...

The results output of this test are :

t in g is lvalue
t in g is rvalue
t in g is lvalue

f(x) get "t in g is lvalue" is just like expected !!

f(X()) get "t in g is rvalue" , yes,that is what std::forward used for

h(X()) get "t in g is lvalue" , this is my question ,as you can see that function h is not a template function , as the author describe "When you combine rvalue references with function templates you get an interesting interaction" is not the case , still this function output "t in g is lvalue" , means this interesting interaction happen not just in template function , also to normal function , too !!

if I change code to :

void h(X &&t)
{
    g(std::forward<X>(t)) ;
}

I will got "t in g is rvalue" !!!

Accorind to test , May I said that the author describe "When you combine rvalue references with function templates you get an interesting interaction" actually not only to template function , it also apply to normal function , or my english is not good , so I can not catch this description right ?!

Edit :

void h(X &&t)
{
    g(t) ;
}

void h(X &t)
{
    g(t) ;
}

h(x);      //get "t in g is lvalue"
h(X());    //get "t in g is lvalue"

=====================================================

void h(X &&t)
{
    g(std::forward<X>(t)) ;
}

void h(X &t)
{
    g(std::forward<X>(t)) ;
}

h(x);      //get "t in g is rvalue"
h(X());    //get "t in g is rvalue"

Look like only in template function , I will get the cprrect usage of std::forward !!!

Was it helpful?

Solution

In h(X &&), the type of t is an r-value reference to X, but named variables are always treated as l-values. So, even though t is an X &&, t can not bind directly to an X && parameter, but only an X & parameter. This is for safety, since a named variable can (and often will) be used over and over again. You don't want the first usage of a variable to pilfer it, even if it did originally bind to an r-value. Subsequent uses would see the pilfered value, which will very easily lead to broken logic in code.

If you know a variable is an r-value (or more to the point, if you know you're done with it, whether it's an l-value or an r-value), the way to pass it on as an r-value is by using move(). The purpose of forward<T>() is for generic code, when you don't know whether the original value should be pilfered or not. If you were to use move() in a template, you may accidentally pilfer an l-value. So you use forward<T>() instead, which will resolve to a harmless pass-through if T is an l-value type, and will essentially become equivalent to move() if T is a non-reference or an r-value reference.

Note that in your edit, your second overload of h (that is, h(X &t)) is using forward<> incorrectly. The type of t in that situation is X &, so you should be using forward<X&>(t). If you did that, you would find that t is passed on as an l-value. However, in your two overloads of h, you can see that in the first, you have an r-value reference, and in the second you have an l-value reference. (There's no template deduction involved, so you know the types.) Therefore, you might as well use move() directly in your first overload, and not use anything in your second. The purpose of forward<T>() is to harvest information from the template deduction to determine whether it was bound to (and deduced as) an l-value or an r-value.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top