In the lecture about universal references, Scott Meyers (at approximately 40th minute) said that objects that are universal references should be converted into real type, before used. In other words, whenever there is a template function with universal reference type, std::forward should be used before operators and expressions are used, otherwise a copy of the object might be made.
My understanding of this is in the following example :
#include <iostream>
struct A
{
A() { std::cout<<"constr"<<std::endl; }
A(const A&) { std::cout<<"copy constr"<<std::endl; }
A(A&&) { std::cout<<"move constr"<<std::endl; }
A& operator=(const A&) { std::cout<<"copy assign"<<std::endl; return *this; }
A& operator=(A&&) { std::cout<<"move assign"<<std::endl; return *this; }
~A() { std::cout<<"destr"<<std::endl; }
void bar()
{
std::cout<<"bar"<<std::endl;
}
};
A getA()
{
A a;
return a;
}
template< typename T >
void callBar( T && a )
{
std::forward< T >( a ).bar();
}
int main()
{
{
std::cout<<"\n1"<<std::endl;
A a;
callBar( a );
}
{
std::cout<<"\n2"<<std::endl;
callBar( getA() );
}
}
As expected, the output is :
1
constr
bar
destr
2
constr
move constr
destr
bar
destr
The question really is why is this needed?
std::forward< T >( a ).bar();
I tried without std::forward, and it seems to work fine (the output is the same).
Similarly, why he recommends to use move inside the function with rvalue? (the answer is the same as for std::forward)
void callBar( A && a )
{
std::move(a).bar();
}
I understand that both std::move and std::forward are just casts to appropriate types, but are these casts really needed in the above example?
Bonus : how can the example be modified to produce the copy of the object that is passed to that function?
What is said in the lecture is this :
After some thought, I realized that it is correct (as expected). Changing the
callBarfunction in the original example to this demonstrate the point :If the
std::forwardwasn’t used incallBar, then thereallyCallBar( A& )would be used. BecauseaincallBaris a lvalue reference.std::forwardmakes it a rvalue, when the universal reference is the rvalue reference.Next modification proves the point even further :
Since
std::moveis not used in thereallyCallBar( A&& ra )function, it doesn’t enter the endless loop. Instead it calls the version taking lvalue reference.Therefore (as explained in the lecture) :
std::forwardmust be used on universal referencesstd::movemust be used on rvalue references