Use of std::forward in c++ - c++

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.

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.

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.

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.

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, /* ... */);

Related

rvalue reference or forwarding reference?

I know that for the following function
template <typename T>
void do_something(T&& arg);
the function parameter is a forwarding reference. But in the following case is it still a forwarding reference or an rvalue reference?
template <typename T>
class MyClass
{
void do_something(T&& arg);
};
I think it is still a forwarding reference, but I'm not sure. Furthermore, I'd like to know what can be done to enforce an rvalue reference or a forwarding reference, if the result is not what I intended.
It's an rvalue reference. Forwarding references can only appear in a deduced context. This is just a member function that accepts an rvalue reference to the class template parameter.
You can't force a forwarding reference to be an rvalue reference if you want to maintain template argument deduction for functions. If you don't mind specifying the template argument all over the place, then this will always and only ever give an rvalue reference:
template<typename T> struct identity { using type = T; };
template<typename T> void func(typename identity<T>::type&&);
In retrospect, there actually is a way to maintain deduction but force only rvalue refs to be accepted (besides the self documenting one in Simple's answer). You can provide a deleted lvalue overload:
template<typename T>
void func(T&) = delete;
template<typename T>
void func(T&& s)
{
// ...
}
The lvalue overload is more specialized when passed an lvalue. And on account of being deleted, will give a somewhat clear error message.
Furthermore I like to know, what can be done to enforce an rvalue reference
If you always want an rvalue reference in a deduced context (and not a forwarding reference), then you can use this:
template<
typename T,
typename = std::enable_if_t<!std::is_lvalue_reference<T>::value>
>
using rval_ref = T&&;
template<typename T>
void foo(rval_ref<T> s)
{
// ...
}
foo can only be called with an rvalue, and T will not be a reference (i.e. if you call foo with std::string&&, then T will be std::string).

Template references type deducion

I have a template function:
template<typename T>
void doSomething(T& value) {
// doSomething here
}
All is ok, but passing r-value references:
doSomething(getTempVal());
Producing no matching function for call error, because particular instantiated function template expects an l-value for 1st argument. Is there any workarounds to allow template function taking both lvalue and rvalue references without adding new template?
Yes, just use && instead of &.
It may seem odd, because you might think that this would disallow calling it with lvalues, but actually, if T is itself an lvalue reference type, then T && is just T: it doesn't become an rvalue reference type.
In other words,
template <typename T>
void f(T &&);
int main() {
int k = 1;
f(k); // okay: calls f<int&>
f(2); // okay: calls f<int>
}
Note that T can be deduced as a reference type, and the function body will need to be made to handle that.

Overload resolution and universal reference parameters

The following code works and the overloads are found as expected:
struct HasBuzz
{
void buzz() const {}
};
struct NoBuzz {};
template <typename T>
void foo(T const& t)
{
t.buzz();
}
void foo(NoBuzz const&){}
int main()
{
foo(HasBuzz{});
foo(NoBuzz{});
}
However, if I replace the first overload with a "universal reference" version then it no longer works. The correct overload is not found for NoBuzz.
struct HasBuzz
{
void buzz() const {}
};
struct NoBuzz {};
template <typename T>
void foo(T&& t)
{
t.buzz();
}
void foo(NoBuzz const&){}
int main()
{
foo(HasBuzz{});
foo(NoBuzz{}); // error: NoBuzz has no member function buzz
}
What can I do to make it work?
Simple solution
Add an overload that is callable with an rvalue of type NoBuzz.
void foo(NoBuzz const&){ };
void foo(NoBuzz&&) { }; // overload for rvalues
Note: Depending on your actual use case this might not be enough, because if you pass a non-const lvalue type NoBuzz to foo you'd still instantiate the template, since the two NoBuzz overloads doesn't match. At the end of this post is a more complex, but certainly cleaner, solution.
Explanation
template<class T>
void foo (T&&); // (A)
void foo (NoBuzz const&); // (B)
The problem with your snippet is that your template (A) can be instantiated in such a way that it's a better match than your overload (B).
When the compiler sees that you are trying to call a function named foo with an argument which is an rvalue of type NoBuzz, it will look for all functions named foo taking one argument where a NoBuzz would fit.
Let's say it starts of with your template (A), here it sees that T&& is deducable to any reference type (both lvalue, and rvalue), since we are passing an rvalue T = NoBuzz.
With T = NoBuzz the instantiated template would be semantically equivalent to:
void foo (NoBuzz&&); // (C), instantiated overload of template (A)
It will then continue to your overload (B). This overload accepts a const lvalue reference, which can bind to both lvalues and rvalues; but our previous template instantiation (C) can only bind to rvalues.
Since (C) is a better match than (B), binding rvalues to T&& is prefered over U const&, that overload is selected and you get the behavior you describe in your post.
Advanced solution
We can use a technique called SFINAE to conditionally make it impossible to call the template if the type passed doesn't implement .buzz ().
template <typename T>
auto foo(T&& t) -> decltype (t.buzz ())
{
return t.buzz();
}
The above solution uses a lot of new features of C++11, detailed information is available here:
wikipedia.org - C++11 - auto
wikipedia.org - decltype
Conditional overloading with trailing-return-type possible?
refp's answer explains the behavior you're seeing, and offers a possible solution. Another option is to ensure the foo function template does not make it into the candidate set for overload resolution unless T has a member function named buzz().
template <typename T>
auto foo(T&& t)
-> decltype((void)(t.buzz()), void())
{
t.buzz();
}
After making this change the foo(NoBuzz const&) overload will be selected when you pass it an instance of NoBuzz. Live demo
A detailed explanation of what's going on in the decltype expression in the trailing return type can be found here. The only thing I've done differently here is instead of using three subexpressions, with the middle one being void() to prevent a user defined operator, from being selected, I've cast the result of the first expression to void; the intent and result are identical in both cases.

Template function as template argument

I'm learning about templates and tried to implement this method:
template <typename Func, typename Left, typename Right>
void flipArgs(Func* function, Left&& leftArg, Right&& rightArg) {
function(std::forward<Right>(rightArg), std::forward<Left>(leftArg));
}
It takes a function and two parameters and calls the given function with the two parameters flipped.
It works fine with function such as:
void test1(std::string, int) {
}
When I tried this function:
template <typename T>
void test2(T&& a, int) {
}
With:
string s("test");
flip(test2<string>, 42, s);
The compiler (g++ 4.7.1) tells me:
error: cannot bind 'std::basic_string' lvalue to 'std::basic_string&&'
I thought that a function parameter such as T&& was a special case that can bind to rvalue and lvalue references? What am I doing wrong?
I thought that a function parameter such as T&& was a special case that can bind to [rvalues and lvalues]?
It is. It basically means the template can have different instantiations for lvalues and for rvalues.
However... When you explicitly make T be string in test2<string>, you are picking one particular instantiation: void test2(string&&, int). string&& is no longer that special case. string&& can only bind to string rvalues. There isn't one instantiation that binds to both rvalues and lvalues.
In general, I'd recommend against explicitly passing function template parameters (unless those are intended, like std::forward or std::make_unique).
In this case, you could instead force one of the instantiations that binds to lvalues. Something like flip(test2<string&>, 42, s);, which will instantiate void test2(string&, int).
If you really want to pass an argument to flip that can accept both lvalues and rvalues, you need a polymorphic function object:
struct test2 {
template <typename T>
void operator()(T&& a, int) const {
}
};
flip(test2{}, 42, s);
The key here is that the decision of which specialisation to use is not made when passing the argument, but only later on when that argument is used.
For completeness, in C++14 you can actually create anonymous polymorphic function objects with the new lambda syntax:
auto test2 = [](auto&& a, int) {};
flip(test2, 42, s);

When you perfect-forward, typename T becomes a T& or T&&, but when you don't, T isn't a reference at all. How?

I'm reading about perfect forwarding, and this is something I've learnt that has confused me: When you're trying to achieve perfect forwarding, you'll do something like this:
template<class T> // If I were to call foo with an l-value int, foo would look
void foo(T &&x); // like this: void foo(int& &&x)
So then I thought, wait, does that mean that if I did this:
template<class T> // If I were to call foo with an l-value int, foo would look
void foo(T x); // like this: void foo(int& x);
But that's not what happens. foo instead looks like this: void foo(int x);
My question: How come in the perfect forwarding function, T turns into a T& or T&&, but in the other one, T isn't a reference? Can somebody tell me the exact rules for this? I need some clarification!
A template parameter T can be deduced as a reference type only if it appears in a function parameter of the form T&&
A function template of the form:
template<class T> void f(T x) will deduce T as an object type (and x is an object type so is passed by value)
template<class T> void f(T& x) will deduce T as an object type (and then x has lvalue reference type)
template<class T> void f(T&& x) will deduce T as
either an lvalue reference (so x has lvalue reference type due to reference collapsing rules)
or as an object type (so x has rvalue reference type)
How come in the perfect forwarding function, T turns into a T& or T&&, [...]
This is wrong. T becomes a reference type L& or an object type R, not a reference R&&.
The function parameter of the form T&& thus becomes
either L& (because adding an rvalue reference to an lvalue reference is still an lvalue reference, just like add_rvalue_reference<L&>::type is still L&)
or it becomes R&& (because add_rvalue_reference<R>::type is R&&)
This is because of the way type deduction is defined, and it is only related to perfect forwarding in the sense that the result of std::forward<>() is an rvalue if an rvalue reference is passed, and an lvalue if an lvalue reference is passed.
But in general, when you do not have a reference to begin with, your T is not going to be deduced as a reference type (i.e. as A&, whatever A could be). If that was the case, as Yakk correctly points out in the comments, it would be impossible to write a function template that takes its arguments by value.
In particular, the reference collapsing rule you are referring to is defined in Paragraph 14.8.2.1/4 of the C++11 Standard:
If P is a
reference type, the type referred to by P is used for type deduction. If P is an rvalue reference to a cv-unqualified
template parameter and the argument is an lvalue, the type “lvalue reference to A” is used in
place of A for type deduction. [ Example:
template <class T> int f(T&&);
template <class T> int g(const T&&);
int i;
int n1 = f(i); // calls f<int&>(int&)
int n2 = f(0); // calls f<int>(int&&)
int n3 = g(i); // error: would call g<int>(const int&&), which
// would bind an rvalue reference to an lvalue
—end example ]
There are three general cases to consider when deducing template parameters like this.
void foo(T x): this means "pass-by-value". It always deduces a type as appropriate to pass by value.
void foo(T& x): this means "pass-by-lvalue-reference". It always deduces a type as appropriate to pass by lvalue reference.
void foo(T&& x): this means "pass-by-reference". It always deduces a type as appropriate to pass by reference, which may be an lvalue reference or an rvalue reference.
Relax, slow down, and breathe.
Template argument deduction is the central mechanism that you need to understand, and it's not totally trivial. When you say template <typename T> void foo(T), then T is always deduced as a non-reference type.
If you want a reference, you have to put a & on it: template <typename T> void foo(T&) will also deduce T as a non-reference-type, but foo now always expects an lvalue reference.
The final piece of magic comes from the new reference collapsing rules. When you say template <typename T> void foo(T&&), then two things can happen:
You call foo with an rvalue, e.g. foo(Bar()). Then T is deduced as Bar, and foo takes an rvalue reference to Bar, i.e. a Bar&&.
You call foo with an lvalue, e.g. Bar x; foo(x);. Now the only thing foo can take is an lvalue reference. This requires T to be deduced as Bar&, since T&& == Bar& && == Bar&, due to the collapsing rules.
Only this final template is able to accept both lvalues and rvalues. This is why it is sometimes called "universal reference"; but remember that it's not the reference that matters, but the template argument deduction. Using std::forward<T> allows you to pass on the argument with the same value category that you received.