SlideShare a Scribd company logo
Rvalue References,
Move Semantics,
Perfect Forwarding
Francesco Casalegno
In this presentation...
1. Understanding fearsome code!
template <typename T, typename... Arg>
std::shared_ptr<T> factory_make(Arg&&... arg) {
return std::shared_ptr<T>(new T(std::forward<Arg>(arg)... ));
}
2. Universal References are not Rvalue References!
3. How forgetting a “noexcept” can wreck your performance!
template<class T>
void foo(std::vector<T>&& v); // this is an rvalue reference…
template<class T>
void foo(T&& v); // this is NOT an rvalue reference!
Vector(Vector&& other); // horrible things may now happen!
2
Lvalues VS Rvalues
According to the Standard:
● lvalue: expression referring to a memory location, allowing to take the address using operator &
● rvalue: everything else, i.e. address may not be taken and cannot use them as l.h.s. in assignment
● (rvalues are further divided into prvalues and xvalues: not discussed today)
Your turn! Lvalues or rvalues?
X foo() { X x(5, "Joe"); return x; }
int a(7);
int b = ++a; // what is ++a ?
int c = b--; // what is b-- ?
X x = X(6, "Jane"); // what is X(6, "Jane") ?
int* v = new int[10];
int d = v[7]; // what is v[7] ?
int e = a+b; // what is a+b ?
X f = foo(); // what is foo() ?
struct bar {
private:
int val_;
public:
…
bar& operator=(bar const& other) {
val_ = other.val_;
return *this; // what is this ?
}
};
3
Lvalue References
Pre C++11 we only had lvalue references
int x =5; int& a = x;
● act as an alias of valid existing object
a = 5; // now also x contains 5!
● used to implement pass-by-reference semantics (avoiding pointers!)
void double_vals(std::vector<int>& v) {...}
● can only bind to lvalues…
std::vector<int> v(10);
double_vals(v); // ok!
double_vals(std::vector<int>(10)); // no!
● … unless they are const lvalue references!
void print_vals(std::vector<int> const& v)
print_vals(std::vector<int>(10)); // ok !
Your turn!
Why would it not make sense for a non-const lvalue reference to bind an rvalue? 4
Rvalue References
In C++11 we also have rvalue references: int&& a = 5;
● can only bind to rvalues: they extend the life of temporary objects
int x = 5;
int& a = 3; // no!
int const& b = 3; // ok!
int&& c = 3; // ok!
int&& d = x; // no!
● unlike const lvalue reference, rvalue reference allow to modify the object!
void print_vals(std::vector<int> const& v); // may take either lvalues or rvalue, but not modify it!
void print_vals(std::vector<int> && v); // may take rvalue, and also modify it!
● in case of function overloading, preferred over const lvalue reference
bool is_temporary_int(int const&) {return false;}
bool is_temporary_int(const&&) {return true;}
Your turn!
What’s the output?
5
int x = 1;
is_temporary_int(3);
is_temporary_int(x);
is_temporary_int(x++);
is_temporary_int(++x);
Move Semantics
Let us consider a class holding some kind of resource:
Pre C++11 we had the Rule of Three: (copy assignment, copy c-tor, d-tor)
● But assignment / copy c-tor can be very expensive for rvalues!
Vec foo() { Vec v(1e9); return v; }
Vec w;
w = foo()
● Potentially, we have:
a. c-tor called for w
b. c-tor called for v
c. copy c-tor called for v into temporary (huge!)
d. copy assignment called for temporary into w (huge!)
● If the compiler is smart, c. may be avoided, but no chance for d. ...
○ However, the temporary returned by foo() is not needed any more...
○ Idea: pilfer the temporary’s data and put it into w without copying values: Move Semantics
○ Rule of Five (move assignment, move c-tor) to pilfer instead of copying data for rvalues
6
class Vec {
int* data_;
size_t size_; …
};
Copy C-tor VS Move C-tor
Copy C-tor
// Copy c-tor:
// 1. allocate space
// 2. copy values from other
Vec(Vec const& other) :
size_(other.size_),
data_(new int[size_])
{
for (int i=0; i<size_; ++i)
data_[i] = other.data_[i];
}
7
Move C-tor
// Move c-tor:
// 1. pilfer data
// 2. set other to nullptr for safe destruction
Vec(Vec&& other) :
size_(other.size_),
data_(other.data_)
{
other.data_ = nullptr;
other.size_ = 0;
}
Copy assignment VS Move assignment: similarly, but check for self-assignment!
Your turn! What happens if we do not set to nullptr after moving?
Forcing move semantics with std::move
Sometimes we have an lvalues, but we want to call the rvalue copy c-tor / assignment.
std::move (#include <utility>) will do the trick by casting to an rvalue reference!
● Ex 1. std::swap
template <class T> void swap (T& a, T& b) {
T c(std::move(a)); a=std::move(b); b=std::move(c);
}
● Ex2. steal ownership for non-assignable and non-copyable (but movable!) objects
std::unique_ptr<double> p1(new double);
std::unique_ptr<double> p2(new double);
p2 = p1; // no!
p2 = std::move(p1); // ok: call d-tor for p1, then pilfer data!
● Ex3. force move semantics by virtue of has-a-name rule: a named rvalue reference is an lvalue
class NamedWrap {
char* nm_;
Vec v_;
NamedWrap(NamedWrap&& other) : // move c-tor
nm_(other.nm_),
v_(std::move(other.v_)) // other is named rvalue reference, without std::move will call copy c-tor!!
{
other.nm_ = nullptr;
}
};
8
When copying is useless: Copy Elision
● The Standard allows the compiler to elide copy or move c-tor–regardless of side effects!–in the following cases:
// 1 . return value optimization
X foo() { X a; return a;}
X p = foo();
// 2. temporary object passed by value
void bar(X x); bar(X(1,"Bob"));
// 3. exception caught by value
void qux() { X c; throw c; }
try{ qux(); }
catch(X except) {some_fun();}
● But then, why do we still need move c-tor?
○ multiple return points and conditional initialization prevent copy/move elision
○ copy/move elision may have desirable side effects: -fno-elide-constructors
○ copy/move elision is only allowed, but not prescribed, by the Standard
○ also, notice that copy/move assignment may not be elided
Your turn! Even using the world's smartest compiler, if we do not implement move c-tor and move
assignment for class X, calling std::sort() on an array of X may take ages. Why? 9
Universal References
● The Standard forbids the construction of a reference to a reference. However, the following is allowed:
template <class T> void foo(T a, T& b);
int x, y;
foo<int&>(x,y); // call to foo(int& , int& &) ??
To solve this, the Standard prescribes collapsing rules:
X & & --> X &
X && & --> X &
X & && --> X &
X && && --> X &&
● Universal reference: && is right-neutral, so it can be used in templates as T&& to match any reference!
template <class T> void foo(T&& a);
int x = 7;
foo(x); // call foo(int& a)
foo(5); // call foo(int&& a)
Your turn!
Do the collapsing rules look similar to a logical operation?
10
Universal reference: && is right-neutral, so it can be
used in templates as T&& with automatic deduction
to match any reference!
Perfect Forwarding with std::forward
● Perfect Forwarding problem: pass an argument arg by lvalue/rvalue reference to foo(), and inside the body of
foo() call bar() with same arg and preserving its nature of lvalue/rvalue reference.
○ Why a problem? arg has-a-name, therefore is always an lvalue for bar!
● Solution: std::forward(arg) (#include <utility>) returns an rvalue reference to arg if arg is not an lvalue reference, and
otherwise it returns arg without modifying its type.
● Classical application: factory pattern
11
bool is_temp(int const&) {return false;}
bool is_temp(int &&) {return true;}
template <class T>
void print_is_temp(T&& arg) {
std::cout << is_temp(arg) << std::endl; // attention! arg has-a-name...
}
print_is_temp(5); // print false!
...
std::cout << is_temp(std::forward(arg)) << std::endl;
}
print_is_temp(5); // print true!
template <typename T, typename... Arg>
std::shared_ptr<T> factory_make(Arg&&... arg) { // 1. Universal Reference
return std::shared_ptr<T>(new T(std::forward<Arg>(arg)... )); // 2. Forward Argument
}
The noexcept keyword
● The C++11 keyword noexcept is used in the signature of a function to guarantee it will not throw any exception. Unlike
throw() it is checked at compile time and allows optimization that would otherwise be impossible.
● Rule: always make sure that your move c-tor and move assignment are exception safe, and mark these functions as
noexcept!
● If we forget the noexcept for class X, std::vector<X>::resize(int n) will call the copy c-tor and wreck performances!
Your turn!
Why should resize() call the copy c-tor even if the old (smaller size) array is not needed any more after resizing?
12
Take Home Message
1. Implement move c-tor and move assignment following the Rule of Five: usually this is the case for classes with a
X* data_ member!
2. Use std::move to cast to rvalue reference: force Move Semantics (Has-A-Name rule) or steal ownership (if
non-copyable/non-assignable e.g. unique_ptr)
3. Use std::forward to implement Perfect Forwarding: preserve nature of rvalue/lvalue reference when forwarding a
template argument (with Universal References)
4. Do not forget to mark as noexcept both move c-tor and move assignment
13

More Related Content

PDF
[C++] The Curiously Recurring Template Pattern: Static Polymorphsim and Expre...
PDF
Hot С++: Universal References And Perfect Forwarding
PDF
Hot C++: Rvalue References And Move Semantics
PDF
Smart Pointers in C++
PDF
Learn C# Programming - Variables & Constants
PPTX
PDF
PGI CUDA FortranとGPU最適化ライブラリの一連携法
PDF
賣 K8s 的人不敢告訴你的事 (Secrets that K8s vendors won't tell you)
[C++] The Curiously Recurring Template Pattern: Static Polymorphsim and Expre...
Hot С++: Universal References And Perfect Forwarding
Hot C++: Rvalue References And Move Semantics
Smart Pointers in C++
Learn C# Programming - Variables & Constants
PGI CUDA FortranとGPU最適化ライブラリの一連携法
賣 K8s 的人不敢告訴你的事 (Secrets that K8s vendors won't tell you)

What's hot (20)

PDF
Date and Time Module in Python | Edureka
PDF
Object-oriented Programming-with C#
ODP
Linux commands
PPTX
pgday.seoul 2019: TimescaleDB
PPTX
NVMCT #1 ~今さら聞けないSSDの基本~
PDF
Deep C
PPT
Class and Objects in PHP
PDF
Understanding Data Partitioning and Replication in Apache Cassandra
PPT
Object-oriented concepts
PDF
Programmation en C
PPTX
Linker and loader upload
PPTX
Variable and constants in Vb.NET
PDF
C# conventions & good practices
PPTX
Templates in C++
PPTX
Introduction à spring boot
PPTX
1. Arrow Functions | JavaScript | ES6
PPT
Basics of pointer, pointer expressions, pointer to pointer and pointer in fun...
PPTX
Function Pointer
PPTX
Coding standards and guidelines
PPTX
Modern C++
Date and Time Module in Python | Edureka
Object-oriented Programming-with C#
Linux commands
pgday.seoul 2019: TimescaleDB
NVMCT #1 ~今さら聞けないSSDの基本~
Deep C
Class and Objects in PHP
Understanding Data Partitioning and Replication in Apache Cassandra
Object-oriented concepts
Programmation en C
Linker and loader upload
Variable and constants in Vb.NET
C# conventions & good practices
Templates in C++
Introduction à spring boot
1. Arrow Functions | JavaScript | ES6
Basics of pointer, pointer expressions, pointer to pointer and pointer in fun...
Function Pointer
Coding standards and guidelines
Modern C++
Ad

Similar to C++11: Rvalue References, Move Semantics, Perfect Forwarding (20)

PDF
C++ references
PDF
C++ CoreHard Autumn 2018. Concurrency and Parallelism in C++17 and C++20/23 -...
PPTX
C++11 - A Change in Style - v2.0
PDF
Cpp17 and Beyond
PDF
C++ boot camp part 1/2
PDF
C++ Boot Camp Part 1
PDF
Functions And Header Files In C++ | Bjarne stroustrup
PDF
[OLD VERSION, SEE DESCRIPTION FOR THE NEWER VERSION LINK] Hot С++: Universal ...
PPTX
The Future of C++
PPTX
C Programming Homework Help
PDF
C++aptitude questions and answers
PPTX
CPP Homework Help
PPTX
What’s new in .NET
PPTX
Object oriented programming system with C++
PPTX
Namespaces
PDF
Milot Shala - C++ (OSCAL2014)
PPTX
Presentation 5th
PPTX
The Style of C++ 11
C++ references
C++ CoreHard Autumn 2018. Concurrency and Parallelism in C++17 and C++20/23 -...
C++11 - A Change in Style - v2.0
Cpp17 and Beyond
C++ boot camp part 1/2
C++ Boot Camp Part 1
Functions And Header Files In C++ | Bjarne stroustrup
[OLD VERSION, SEE DESCRIPTION FOR THE NEWER VERSION LINK] Hot С++: Universal ...
The Future of C++
C Programming Homework Help
C++aptitude questions and answers
CPP Homework Help
What’s new in .NET
Object oriented programming system with C++
Namespaces
Milot Shala - C++ (OSCAL2014)
Presentation 5th
The Style of C++ 11
Ad

More from Francesco Casalegno (6)

PDF
DVC - Git-like Data Version Control for Machine Learning projects
PDF
Ordinal Regression and Machine Learning: Applications, Methods, Metrics
PDF
Recommender Systems
PDF
Markov Chain Monte Carlo Methods
PDF
Hyperparameter Optimization for Machine Learning
PDF
Confidence Intervals––Exact Intervals, Jackknife, and Bootstrap
DVC - Git-like Data Version Control for Machine Learning projects
Ordinal Regression and Machine Learning: Applications, Methods, Metrics
Recommender Systems
Markov Chain Monte Carlo Methods
Hyperparameter Optimization for Machine Learning
Confidence Intervals––Exact Intervals, Jackknife, and Bootstrap

Recently uploaded (20)

PPT
Teaching material agriculture food technology
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
Encapsulation_ Review paper, used for researhc scholars
PPTX
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
PDF
Encapsulation theory and applications.pdf
PPTX
MYSQL Presentation for SQL database connectivity
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PPTX
Big Data Technologies - Introduction.pptx
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PDF
Machine learning based COVID-19 study performance prediction
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PDF
cuic standard and advanced reporting.pdf
PPTX
Cloud computing and distributed systems.
DOCX
The AUB Centre for AI in Media Proposal.docx
PPTX
Programs and apps: productivity, graphics, security and other tools
Teaching material agriculture food technology
Mobile App Security Testing_ A Comprehensive Guide.pdf
Unlocking AI with Model Context Protocol (MCP)
Encapsulation_ Review paper, used for researhc scholars
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
Dropbox Q2 2025 Financial Results & Investor Presentation
NewMind AI Weekly Chronicles - August'25 Week I
20250228 LYD VKU AI Blended-Learning.pptx
Encapsulation theory and applications.pdf
MYSQL Presentation for SQL database connectivity
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Big Data Technologies - Introduction.pptx
Digital-Transformation-Roadmap-for-Companies.pptx
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
Machine learning based COVID-19 study performance prediction
Understanding_Digital_Forensics_Presentation.pptx
cuic standard and advanced reporting.pdf
Cloud computing and distributed systems.
The AUB Centre for AI in Media Proposal.docx
Programs and apps: productivity, graphics, security and other tools

C++11: Rvalue References, Move Semantics, Perfect Forwarding

  • 1. Rvalue References, Move Semantics, Perfect Forwarding Francesco Casalegno
  • 2. In this presentation... 1. Understanding fearsome code! template <typename T, typename... Arg> std::shared_ptr<T> factory_make(Arg&&... arg) { return std::shared_ptr<T>(new T(std::forward<Arg>(arg)... )); } 2. Universal References are not Rvalue References! 3. How forgetting a “noexcept” can wreck your performance! template<class T> void foo(std::vector<T>&& v); // this is an rvalue reference… template<class T> void foo(T&& v); // this is NOT an rvalue reference! Vector(Vector&& other); // horrible things may now happen! 2
  • 3. Lvalues VS Rvalues According to the Standard: ● lvalue: expression referring to a memory location, allowing to take the address using operator & ● rvalue: everything else, i.e. address may not be taken and cannot use them as l.h.s. in assignment ● (rvalues are further divided into prvalues and xvalues: not discussed today) Your turn! Lvalues or rvalues? X foo() { X x(5, "Joe"); return x; } int a(7); int b = ++a; // what is ++a ? int c = b--; // what is b-- ? X x = X(6, "Jane"); // what is X(6, "Jane") ? int* v = new int[10]; int d = v[7]; // what is v[7] ? int e = a+b; // what is a+b ? X f = foo(); // what is foo() ? struct bar { private: int val_; public: … bar& operator=(bar const& other) { val_ = other.val_; return *this; // what is this ? } }; 3
  • 4. Lvalue References Pre C++11 we only had lvalue references int x =5; int& a = x; ● act as an alias of valid existing object a = 5; // now also x contains 5! ● used to implement pass-by-reference semantics (avoiding pointers!) void double_vals(std::vector<int>& v) {...} ● can only bind to lvalues… std::vector<int> v(10); double_vals(v); // ok! double_vals(std::vector<int>(10)); // no! ● … unless they are const lvalue references! void print_vals(std::vector<int> const& v) print_vals(std::vector<int>(10)); // ok ! Your turn! Why would it not make sense for a non-const lvalue reference to bind an rvalue? 4
  • 5. Rvalue References In C++11 we also have rvalue references: int&& a = 5; ● can only bind to rvalues: they extend the life of temporary objects int x = 5; int& a = 3; // no! int const& b = 3; // ok! int&& c = 3; // ok! int&& d = x; // no! ● unlike const lvalue reference, rvalue reference allow to modify the object! void print_vals(std::vector<int> const& v); // may take either lvalues or rvalue, but not modify it! void print_vals(std::vector<int> && v); // may take rvalue, and also modify it! ● in case of function overloading, preferred over const lvalue reference bool is_temporary_int(int const&) {return false;} bool is_temporary_int(const&&) {return true;} Your turn! What’s the output? 5 int x = 1; is_temporary_int(3); is_temporary_int(x); is_temporary_int(x++); is_temporary_int(++x);
  • 6. Move Semantics Let us consider a class holding some kind of resource: Pre C++11 we had the Rule of Three: (copy assignment, copy c-tor, d-tor) ● But assignment / copy c-tor can be very expensive for rvalues! Vec foo() { Vec v(1e9); return v; } Vec w; w = foo() ● Potentially, we have: a. c-tor called for w b. c-tor called for v c. copy c-tor called for v into temporary (huge!) d. copy assignment called for temporary into w (huge!) ● If the compiler is smart, c. may be avoided, but no chance for d. ... ○ However, the temporary returned by foo() is not needed any more... ○ Idea: pilfer the temporary’s data and put it into w without copying values: Move Semantics ○ Rule of Five (move assignment, move c-tor) to pilfer instead of copying data for rvalues 6 class Vec { int* data_; size_t size_; … };
  • 7. Copy C-tor VS Move C-tor Copy C-tor // Copy c-tor: // 1. allocate space // 2. copy values from other Vec(Vec const& other) : size_(other.size_), data_(new int[size_]) { for (int i=0; i<size_; ++i) data_[i] = other.data_[i]; } 7 Move C-tor // Move c-tor: // 1. pilfer data // 2. set other to nullptr for safe destruction Vec(Vec&& other) : size_(other.size_), data_(other.data_) { other.data_ = nullptr; other.size_ = 0; } Copy assignment VS Move assignment: similarly, but check for self-assignment! Your turn! What happens if we do not set to nullptr after moving?
  • 8. Forcing move semantics with std::move Sometimes we have an lvalues, but we want to call the rvalue copy c-tor / assignment. std::move (#include <utility>) will do the trick by casting to an rvalue reference! ● Ex 1. std::swap template <class T> void swap (T& a, T& b) { T c(std::move(a)); a=std::move(b); b=std::move(c); } ● Ex2. steal ownership for non-assignable and non-copyable (but movable!) objects std::unique_ptr<double> p1(new double); std::unique_ptr<double> p2(new double); p2 = p1; // no! p2 = std::move(p1); // ok: call d-tor for p1, then pilfer data! ● Ex3. force move semantics by virtue of has-a-name rule: a named rvalue reference is an lvalue class NamedWrap { char* nm_; Vec v_; NamedWrap(NamedWrap&& other) : // move c-tor nm_(other.nm_), v_(std::move(other.v_)) // other is named rvalue reference, without std::move will call copy c-tor!! { other.nm_ = nullptr; } }; 8
  • 9. When copying is useless: Copy Elision ● The Standard allows the compiler to elide copy or move c-tor–regardless of side effects!–in the following cases: // 1 . return value optimization X foo() { X a; return a;} X p = foo(); // 2. temporary object passed by value void bar(X x); bar(X(1,"Bob")); // 3. exception caught by value void qux() { X c; throw c; } try{ qux(); } catch(X except) {some_fun();} ● But then, why do we still need move c-tor? ○ multiple return points and conditional initialization prevent copy/move elision ○ copy/move elision may have desirable side effects: -fno-elide-constructors ○ copy/move elision is only allowed, but not prescribed, by the Standard ○ also, notice that copy/move assignment may not be elided Your turn! Even using the world's smartest compiler, if we do not implement move c-tor and move assignment for class X, calling std::sort() on an array of X may take ages. Why? 9
  • 10. Universal References ● The Standard forbids the construction of a reference to a reference. However, the following is allowed: template <class T> void foo(T a, T& b); int x, y; foo<int&>(x,y); // call to foo(int& , int& &) ?? To solve this, the Standard prescribes collapsing rules: X & & --> X & X && & --> X & X & && --> X & X && && --> X && ● Universal reference: && is right-neutral, so it can be used in templates as T&& to match any reference! template <class T> void foo(T&& a); int x = 7; foo(x); // call foo(int& a) foo(5); // call foo(int&& a) Your turn! Do the collapsing rules look similar to a logical operation? 10 Universal reference: && is right-neutral, so it can be used in templates as T&& with automatic deduction to match any reference!
  • 11. Perfect Forwarding with std::forward ● Perfect Forwarding problem: pass an argument arg by lvalue/rvalue reference to foo(), and inside the body of foo() call bar() with same arg and preserving its nature of lvalue/rvalue reference. ○ Why a problem? arg has-a-name, therefore is always an lvalue for bar! ● Solution: std::forward(arg) (#include <utility>) returns an rvalue reference to arg if arg is not an lvalue reference, and otherwise it returns arg without modifying its type. ● Classical application: factory pattern 11 bool is_temp(int const&) {return false;} bool is_temp(int &&) {return true;} template <class T> void print_is_temp(T&& arg) { std::cout << is_temp(arg) << std::endl; // attention! arg has-a-name... } print_is_temp(5); // print false! ... std::cout << is_temp(std::forward(arg)) << std::endl; } print_is_temp(5); // print true! template <typename T, typename... Arg> std::shared_ptr<T> factory_make(Arg&&... arg) { // 1. Universal Reference return std::shared_ptr<T>(new T(std::forward<Arg>(arg)... )); // 2. Forward Argument }
  • 12. The noexcept keyword ● The C++11 keyword noexcept is used in the signature of a function to guarantee it will not throw any exception. Unlike throw() it is checked at compile time and allows optimization that would otherwise be impossible. ● Rule: always make sure that your move c-tor and move assignment are exception safe, and mark these functions as noexcept! ● If we forget the noexcept for class X, std::vector<X>::resize(int n) will call the copy c-tor and wreck performances! Your turn! Why should resize() call the copy c-tor even if the old (smaller size) array is not needed any more after resizing? 12
  • 13. Take Home Message 1. Implement move c-tor and move assignment following the Rule of Five: usually this is the case for classes with a X* data_ member! 2. Use std::move to cast to rvalue reference: force Move Semantics (Has-A-Name rule) or steal ownership (if non-copyable/non-assignable e.g. unique_ptr) 3. Use std::forward to implement Perfect Forwarding: preserve nature of rvalue/lvalue reference when forwarding a template argument (with Universal References) 4. Do not forget to mark as noexcept both move c-tor and move assignment 13