Move Semantics
Perfect Forwarding
File: foward1.cpp
#include <iostream> using namespace std ; class Object { public: Object() = default; void SetName(const string &name) { name_ = move(name); } string GetName() const { return name_; } private: string name_; }; void UseObject(Object& x1) { cout << "calling UseObject(Object &)" << endl; } void UseObject(const Object& x1) { cout << "calling UseObject(const Object &)" << endl; } void UseObject(Object&& x1) { cout << "calling UseObject(Object &&)" << endl; } template <typename T> void NotForwardToUseObject(T x) { UseObject(x); } template <typename T> void ForwardToUseObject(T &&x) { UseObject(static_cast<T &&>(x)); } template <typename T> void PerfectForwardToUseObject(T &&x) { UseObject(forward<T>(x)); } int main() { Object object; const Object const_object; UseObject(object); UseObject(const_object); UseObject(move(object)); cout << "----" << endl; NotForwardToUseObject(object); NotForwardToUseObject(const_object); NotForwardToUseObject(move(object)); cout << "----" << endl; ForwardToUseObject(object); ForwardToUseObject(const_object); ForwardToUseObject(move(object)); cout << "----" << endl; PerfectForwardToUseObject(object); PerfectForwardToUseObject(const_object); PerfectForwardToUseObject(move(object)); } $ rm a.exe ; g++ forward1.cpp ; ./a.exe calling UseObject(Object &) calling UseObject(const Object &) calling UseObject(Object &&) ---- calling UseObject(Object &) calling UseObject(Object &) calling UseObject(Object &) ---- calling UseObject(Object &) calling UseObject(const Object &) calling UseObject(Object &&) ---- calling UseObject(Object &) calling UseObject(const Object &) calling UseObject(Object &&)
There are certain cases where we call a function that
can take a lvalue or rvalue reference.In the above file
we can see such a function:
void UseObject(Object& x1)
{
cout << "calling UseObject(Object &)" << endl;
}
void UseObject(const Object& x1)
{
cout << "calling UseObject(const Object &)" << endl;
}
void UseObject(Object&& x1)
{
cout << "calling UseObject(Object &&)" << endl;
}
template
void NotForwardToUseObject(T x)
{
UseObject(x);
}
We want that the right "UseObject" should be called
when we call a common function such as "NotForwardToUseObject" .
We want the "UseObject" to be called for lvalue or rvalue references.
However we know that as long as we have a name it is a lvalue. To
preserve the concept of lvalue and rvalue we have the concept of
perfect forwarding.
template
void PerfectForwardToUseObject(T&& x) {
UseObject(forward(x));
}
The syntax is not very intuitive or logical but we define a
template function and the argument as "T&& x".
Now with the "forward" function we can preserve the lvalue or
rvalue of the argument.
Exercise
1)Explain the following program and output.
File: foward_ex1.cpp
#include <iostream> using namespace std ; class Object { public: Object() = default; void SetName(const string &name) { name_ = move(name); } string GetName() const { return name_; } private: string name_; }; void UseObject(Object& x1) { cout << "calling UseObject(Object &)" << endl; } void UseObject(const Object& x1) { cout << "calling UseObject(const Object &)" << endl; } void UseObject(Object&& x1) { cout << "calling UseObject(Object &&)" << endl; } template <typename T> void NotForwardToUseObject(T x) { UseObject(x); } template <typename T> void ForwardToUseObject(T &&x) { UseObject(static_cast<T &&>(x)); } template <typename T> void PerfectForwardToUseObject(T &&x) { //UseObject(forward<T>(x)); UseObject( x ); } template <typename T> void PerfectForwardToUseObject1(T &&x) { UseObject(forward<T>(x)); } int main() { Object object; const Object const_object; cout << "----" << endl; PerfectForwardToUseObject(object); PerfectForwardToUseObject(const_object); PerfectForwardToUseObject(move(object)); PerfectForwardToUseObject1( Object() ); cout << "----" << endl; } $ g++ forward_ex1.cpp ; ./a.exe ---- calling UseObject(Object &) calling UseObject(const Object &) calling UseObject(Object &) calling UseObject(Object &&) ----