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?