17

I have come across a code, where std::forward is used. I have googled about it for a longtime and not able to understand its real purpose and use.

I have seen similar threads in stackoverflow, but still not clear. Can somebody explain it with a simple example?

PS: I have gone through this page, but still not able to appreciate its use. Please do not flag this question duplicate and rather try to help me out.

SKPS
  • 5,433
  • 5
  • 29
  • 63
  • 1
    [Read this question and answer](http://stackoverflow.com/questions/3582001/advantages-of-using-forward). GMan's answer and expansion on what it is and how to use it is positively outstanding. Your question is (unfortunately) a duplicate of that one, but just read it none-the-less. – WhozCraig Sep 04 '13 at 16:23
  • 1
    I would suggest the reading of ["C++ Rvalue References Explained"](http://thbecker.net/articles/rvalue_references/section_01.html) by Thomas Becker, in particular, the section on perfect forwarding. – Cassio Neri Sep 04 '13 at 16:28
  • @CassioNeri Thanks. In fact I had difficulty in understanding lvalues and rvalues – SKPS Sep 04 '13 at 16:32

4 Answers4

25

As the page you linked poses it:

This is a helper function to allow perfect forwarding of arguments taken as rvalue references to deduced types, preserving any potential move semantics involved.

When you have a named value, as in

void f1(int& namedValue){
    ...
}

or in

void f2(int&& namedValue){
    ...
}

it evaluates, no matter what, to an lvalue.

One more step. Suppose you have a template function

template <typename T>
void f(T&& namedValue){
    ...
}

such function can either be called with an lvalue or with an rvalue; however, no matter what, namedValue evaluates to an lvalue.

Now suppose you have two overloads of an helper function

void helper(int& i){
    ...
}
void helper(int&& i){
    ...
}

calling helper from inside f

template <typename T>
void f(T&& namedValue){
    helper(namedValue);
}

will invariably call the first overload for helper, since namedValue is, well, a named value which, naturally, evaluates to an lvalue.

In order to get the second version called when appropriate (i.e. when f has been invoked with a rvalue parameter), you write

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

All of this is expressed much concisely in the documentation by the following

The need for this function stems from the fact that all named values (such as function parameters) always evaluate as lvalues (even those declared as rvalue references), and this poses difficulties in preserving potential move semantics on template functions that forward arguments to other functions.

Gelldur
  • 11,187
  • 7
  • 57
  • 68
Stefano Falasca
  • 8,837
  • 2
  • 18
  • 24
  • You last example, while it will work, should instead use `std::move`. It isn't a deduced type, and isn't an example of the purpose of `std::forward`. – Andrew Tomazos Sep 04 '13 at 16:38
  • @user1131467 using `std::move` would (potentially) invalidate `namedValue`, hence impeding its use when `helper(std::move(namedValue))` returns. – Stefano Falasca Sep 04 '13 at 16:42
  • As will `std::forward`. `std::forward` has exactly the same effect as `std::move` in your example, it always produces an xvalue. – Andrew Tomazos Sep 04 '13 at 16:43
  • @user1131467 In the documentation for `std::move` I read: the value of the moved-from object should only be destroyed or assigned a new value. Is there anything similar in the documentation for `std::forward`? – Stefano Falasca Sep 04 '13 at 16:45
  • @user1131467 ok, thanks, please give me some feedback for the revised version – Stefano Falasca Sep 04 '13 at 17:02
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/36807/discussion-between-stefano-falasca-and-user1131467) – Stefano Falasca Sep 04 '13 at 17:03
  • 1
    Now that you have changed it from `int&&` to a deduced type "universal reference" `T&&`, it is now a correct use of `std::forward`. – Andrew Tomazos Sep 04 '13 at 17:10
5

Each expression is in exactly one of the following two value categories: lvalue or rvalue.

Normally if you call a function like:

template<typename T>
void f(T t);

template<typename T>
void g(T t)
{
    f(t);
}

The value category of the argument to g is lost between the call to g and f, because named parameters, like local variables, are always lvalues.

By using std::forward and adjusting the parameter to a "universal reference" that uses reference collapsing you can preserve the value category:

template<typename T>
void f(T&& t);

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

That's why it's called "forward", because you are "forwarding" the value category on, rather than losing it.

So in the example if you call g with an rvalue, then f will be called with an rvalue - rather than an lvalue.

Andrew Tomazos
  • 66,139
  • 40
  • 186
  • 319
1

It is used to preserve the exact type of an argument in templates when passing it to another function. For example:

template<class T>
void wrapper(T&& arg)
{
    foo(std::forward<T>(arg)); // Forward a single argument.
}

This works as follows:

  • If the function wrapper gets a std::string or const std::string&, then foo is called as if arg has type of const std::string&.

  • If the function wrapper gets a std::string&, then foo is called as if arg has type of std::string&.

  • If the function wrapper gets a std::string&&, then foo is called as if arg has type of std::string&&.

The problem that std::forward solves is that by the usual rules the type of arg within function is std::string even if we pass std::string&& to wrapper. std::forward allows to inject the actual type of T, be it T, T&, const T& or T&&, to the call site.

  • Pretty close, the function gets a prvalue, xvalue or lvalue - expressions (arguments) can never have reference type - so a function can never gets a "string&" or "string&&" as an argument. – Andrew Tomazos Sep 04 '13 at 16:27
  • Sorry, it's still not correct. If I have `string&& arg = ...; wrapper(arg);`, arg is an lvalue, and the forward will be as if `static_cast(arg)`, not `static_cast(arg)`. – Andrew Tomazos Sep 04 '13 at 16:34
0

It's basic use is you're in function g that has been called like this:

g(T1 p1, T2 p2, /* ... */);

and you want to call function f with exactly the same types:

f(T1 p1, T2 p2, /* ... */);
Paul Evans
  • 27,315
  • 3
  • 37
  • 54