Module 51
Module 51
L
Partha Pratim
Das
Programming in Modern C++
E
Weekly Recap
Objectives & Module M51: C++11 and beyond: General Features: Part 6: Rvalue & Perfect Forwarding
T
Outlines
Universal
P
References
Recap
T&&
Partha Pratim Das
N
auto
Rvalue vs. Universal
References
Department of Computer Science and Engineering
Perfect
Forwarding Indian Institute of Technology, Kharagpur
Type Safety
Practice Examples [email protected]
std::forward
Move is an All url’s in this module have been accessed in September, 2021 and found to be functional
Optimization of
Copy
Compiler Generated
Move
Module Summary
Programming in Modern C++ Partha Pratim Das M51.1
Weekly Recap
Module M51
• Introduced several C++11 general features:
L
Partha Pratim
Das ◦ auto / decltype
◦ suffix return type (+ C++14)
◦ Initializer List
E
Weekly Recap
Objectives &
◦ Uniform Initialization
◦ Range for Statement
T
Outlines
Universal
◦ constexpr (+ C++14)
◦ noexcept
P
References
Recap ◦ nullptr
T&& ◦ Inline namespace
◦ static assert
N
auto
Rvalue vs. Universal
References
◦ User-defined Literals (+ C++14)
◦ Digit Separators and Binary Literals (+ C++14)
Perfect
Forwarding
◦ Raw String Literals
◦ Unicode Support
Type Safety
Practice Examples
◦ Memory Alignment
◦ Attributes (+ C++14)
std::forward
• Understood the difference between Copying & Moving and Lvalue & Rvalue
Move is an
Optimization of
• Learnt the advantages of Move in C++11 using Rvalue Reference, Move Semantics, and Copy / Move Constructor /
Copy Assignment
Compiler Generated • Learnt to implement move semantics in UDTs using std::move and to implement std::move
Move • Studied a project to code move-enabled UDTs
Module Summary
Programming in Modern C++ Partha Pratim Das M51.2
Module Objectives
Module M51
L
Partha Pratim
Das
type deduction
E
Weekly Recap
• To understand the problem of forwarding of parameters under template type deduction
Objectives &
T
Outlines
• To learn how Universal Reference and std::forward can work for perfect forwarding
Universal
of parameters under template type deduction
P
References
Recap
T&& • To understand the implementation of std::forward
N
auto
Rvalue vs. Universal • To understand how Move is an optimization of Copy
References
Perfect
Forwarding
Type Safety
Practice Examples
std::forward
Move is an
Optimization of
Copy
Compiler Generated
Move
Module Summary
Programming in Modern C++ Partha Pratim Das M51.3
Module Outline
Module M51
1 Weekly Recap
L
Partha Pratim
Das
2 Universal References
E
Weekly Recap
Recap
Objectives &
T
Outlines T&& is Universal Reference
Universal auto is Universal Reference
P
References
Recap Rvalue vs. Universal References
T&&
N
auto
Rvalue vs. Universal
3 Perfect Forwarding
References
Type Safety
Perfect
Forwarding Practice Examples
Type Safety
Practice Examples 4 std::forward
std::forward
Module M51
Sources:
•
L
Partha Pratim
Das
Universal References in C++11 – Scott Meyers, isocpp.org, 2012
• An Overview of the New C++ (C++11/14), Scott Meyers Training Courses
• Scott Meyers on C++
E
Weekly Recap
Objectives &
• Understanding Move Semantics and Perfect Forwarding: Part 1, Part 2: Rvalue References and Move Semantics, Part
3: Perfect Forwarding, Drew Campbell, 2018
T
Outlines
Universal
P
References
Recap
T&&
N
auto
Rvalue vs. Universal
References
Perfect
Forwarding
Type Safety
Practice Examples
std::forward
Move is an
Universal References
Optimization of
Copy
Compiler Generated
Move
Module Summary
Programming in Modern C++ Partha Pratim Das M51.5
Reference Collapsing in Templates: Recap (Module 50)
Module M51
• In C++03, given
L
Partha Pratim template<typename T> void f(T& param);
Das
int x;
E
Weekly Recap f<int&>(x); // T is int&
Objectives & • f is initially instantiated as
T
Outlines
Universal
void f(int& & param); // reference to reference
• C++03’s reference-collapsing rule says
P
References
Recap
T&& ◦ T& & => T&
N
auto
Rvalue vs. Universal • So, after reference collapsing, f’s instantiation is actually: void f(int& param);
References
• C++11’s rules take rvalue references into account:
Perfect
Forwarding ◦ T& & => T& // from C++03
Type Safety ◦ T&& & => T& // new for C++11
Practice Examples ◦ T& && => T& // new for C++11
std::forward ◦ T&& && => T&& // new for C++11
Move is an
Optimization of
• Summary:
Copy
Compiler Generated
◦ Reference collapsing involving a & is always T&
Move
◦ Reference collapsing involving only && is T&&
Module Summary
Programming in Modern C++ Partha Pratim Das M51.6
T&& Parameter Deduction in Templates: Recap (Module 50)
Module M51
• Function templates with a T&& parameter need not generate functions taking a T&& parameter!
L
Partha Pratim
Das
E
Weekly Recap
Objectives &
T
Outlines
• T’s deduced type depends on what is passed to param:
Universal
◦ Lvalue ⇒ T is an lvalue reference (T&)
P
References
Recap
T&&
◦ Rvalue ⇒ T is a non-reference (T)
N
auto
Rvalue vs. Universal • In conjunction with reference collapsing:
References
Perfect
Forwarding int x;
Type Safety
f(x); // lvalue: generates f<int&>(int& &&), calls f(int&)
Practice Examples
f(10); // rvalue: generates f<int>(int&&), calls f(int&&)
std::forward
Module M51
L
Partha Pratim
Das
◦ For lvalue arguments, T&& becomes T& => lvalues can bind
E
Weekly Recap
◦ For rvalue arguments, T&& remains T&& => rvalues can bind
Objectives &
◦ For const/volatile arguments, const/volatile becomes part of T
T
Outlines
P
References
Recap • Two conceptual meanings for T&& syntax:
T&&
◦ Rvalue reference. Binds rvalues only
N
auto
Rvalue vs. Universal
References
Move is an
template<typename T>
Optimization of void f(T&& param); // takes lvalue or rvalue, const or non-const
Copy
Compiler Generated
Move
. Really an rvalues reference in a reference-collapsing context
Module Summary
Programming in Modern C++ Partha Pratim Das M51.8
auto&& ≡ T&&
Module M51
• auto type deduction ≡ template type deduction, so auto&& variables are also universal
L
Partha Pratim
Das
references:
E
Weekly Recap
T
Outlines
int x;
Universal
P
References
Recap auto&& v1 = calcVal(); // deduce type from rvalue => v1’s type is int&&
T&&
N
auto
Rvalue vs. Universal
auto&& v2 = x; // deduce type from lvalue => v2’s type is int&
References
Perfect
Forwarding • Note that decltype()&& does not behave like a universal references as it does not use
Type Safety
Practice Examples
template type deduction:
std::forward
decltype(calcVal()) v3; // deduced type is int
Move is an
Optimization of
Copy
decltype(x) v4; // deduced type is int
Compiler Generated
Move
Module Summary
Programming in Modern C++ Partha Pratim Das M51.9
Rvalue References vs. Universal References
Module M51
• Read code carefully to distinguish them
L
Partha Pratim
Das ◦ Both use && syntax: Occus after a POD or UDT for Rvalue References, but after
type variable T for Universal References
E
Weekly Recap
Objectives &
◦ Type deduction for T for Universal References
T
Outlines
◦ Behavior is different:
Universal
. Rvalue references bind only rvalues
P
References
Recap
T&&
. Universal references bind lvalues and rvalues
N
auto
Rvalue vs. Universal
− that is, may become either T& or T&&, depending on initializer
References
Perfect
• Consider std::vector:
Forwarding template<class T, class Allocator=allocator<T>> // from C++11 Standard
Type Safety
Practice Examples
class vector { public: ...
void push_back(const T& x); // lvalue reference
std::forward
void push_back(T&& x); // rvalue reference!
Move is an
Optimization of template<class... Args>
Copy void emplace_back(Args&&... args); // universal reference
Compiler Generated
Move ...
Module Summary
};
Programming in Modern C++ Partha Pratim Das M51.10
Rvalue Ref. vs. Universal Ref.: Illustration Post-Recording
Module M51 #include <iostream> void g(int&& param) // simple fn - rvalue ref
using namespace std; { test(forward<int>(param)); }
L
Partha Pratim
Das
// overloaded functions for test template<typename T>
void test(const int& a) // lvalue ref void f(T&& param) // template fn - universal ref
E
Weekly Recap
{ cout << "lvalue:" << a << endl; } { test(forward<T>(param)); }
Objectives & void test(int&& a) // rvalue ref
T
Outlines
{ cout << "rvalue: " << a << endl; } int main() { int a = 20;
Universal //g(a); // cannot bind rvalue reference of
P
References template <typename T> // type int&& to lvalue of type int
Recap
class Data { T _data; public: g(move(a)); // rvalue: 20
T&&
Data(T data): _data(data) f(a); // lvalue: 20
N
auto
Rvalue vs. Universal
{ cout << "ctor " << endl; } f(move(a)); // rvalue: 20
References Data(Data&& obj) // move ctor - rvalue ref
Perfect { cout << "mtor " << endl; } Data<int> d1(10); // ctor
Forwarding // class template Data<int> d2(move(d1)); // mtor: rvalue ref
Type Safety void f1(T&& v) { // rvalue ref //d1.f1(a); // cannot bind rvalue reference of
Practice Examples test(forward<T>(v)); // type int&& to lvalue of type int
std::forward } d1.f1(move(a)); // rvalue: 20
template<typename U> // member fn. template d1.f2(a); // lvalue: 20
Move is an
Optimization of void f2(U&& v) { // universal ref d1.f2(move(a)); // rvalue: 20
Copy test(forward<U>(v)); // }
Compiler Generated }
Move
}; // For std::forward - See Perfect Forwarding
Module Summary
Programming in Modern C++ Partha Pratim Das M51.11
Perfect Forwarding
Module M51
Sources:
•
L
Partha Pratim
Das
An Overview of the New C++ (C++11/14), Scott Meyers Training Courses
• Scott Meyers on C++
• Perfect Forwarding, modernescpp.com, 2016
E
Weekly Recap
Objectives &
• Understanding Move Semantics and Perfect Forwarding: Part 1, Part 2: Rvalue References and Move Semantics, Part
3: Perfect Forwarding, Drew Campbell, 2018
T
Outlines
Universal
P
References
Recap
T&&
N
auto
Rvalue vs. Universal
References
Perfect
Forwarding
Type Safety
Practice Examples
std::forward
Move is an
Perfect Forwarding
Optimization of
Copy
Compiler Generated
Move
Module Summary
Programming in Modern C++ Partha Pratim Das M51.12
Perfect Forwarding
Module M51
L
Partha Pratim
Das
◦ Copies lvalue args
E
Weekly Recap
◦ Moves rvalue args
Objectives &
T
Outlines • Solution is a perfect forwarding function:
Universal
◦ Templatized function forwarding T&& params to members
P
References
Recap
T&& • What is Perfect Forwarding?
N
auto
Rvalue vs. Universal ◦ Perfect forwarding allows a template function that accepts a set of arguments to
References
Perfect
forward these arguments to another function whilst retaining the lvalue or rvalue
Forwarding nature of the original function arguments
Type Safety
Practice Examples
• Let us check an example
std::forward
Move is an
Optimization of
Copy
Compiler Generated
Move
Module Summary
Programming in Modern C++ Partha Pratim Das M51.13
Perfect Forwarding Example: (broken)
#include <iostream>
Module M51
class Data { int i; public: Data(): i(0) { } }; // a UDT
L
Partha Pratim
Das void g(const int&) { std::cout << "int& in g" << "; "; } // binds with lvalue parameter
void g(int&&) { std::cout << "int&& in g" << "; "; } // binds with rvalue parameter
E
Weekly Recap
Objectives & void h(const Data&) { std::cout << "Data& in h" << std::endl; } // binds with lvalue parameter
T
Outlines void h(Data&&) { std::cout << "Data&& in h" << std::endl; } // binds with rvalue parameter
Universal
P
References template<typename T1, typename T2>
Recap void f(T1&& p1, T2&& p2) { // universal ref. gets lvalue or rvalue from arg by template type deduction
T&& g(p1); // always binds with lvalue parameter as p1 is an lvalue in f
N
auto
Rvalue vs. Universal
h(p2); // always binds with lvalue parameter as p2 is an lvalue in f
References }
Perfect
int main() { int i { 0 }; Data d;
Forwarding f(i, d); // (lvalue, lvalue) binds with int& in g; Data& in h
Type Safety f(std::move(i), d); // (rvalue, lvalue) binds with int& in g; Data& in h
Practice Examples f(i, std::move(d)); // (lvalue, rvalue) binds with int& in g; Data& in h
std::forward
f(std::move(i), std::move(d)); // (rvalue, rvalue) binds with int& in g; Data& in h
}
Move is an
Optimization of • Lvalue arg passed to p1 ⇒ g(const int&) receives Lvalue
Copy
Compiler Generated
• Rvalue arg passed to p1 ⇒ g(int&&) receives Lvalue
Move • Lvalue arg passed to p2 ⇒ h(const Data&) receives Lvalue
Module Summary • Rvalue arg passed to p2 ⇒ h(Data&&) receives Lvalue
Programming in Modern C++ Partha Pratim Das M51.14
Perfect Forwarding Example: (fixed) by std::forward
#include <iostream>
Module M51
class Data { int i; public: Data(): i(0) { } }; // a UDT
L
Partha Pratim
Das void g(const int&) { std::cout << "int& in g" << "; "; } // binds with lvalue parameter
void g(int&&) { std::cout << "int&& in g" << "; "; } // binds with rvalue parameter
E
Weekly Recap
Objectives & void h(const Data&) { std::cout << "Data& in h" << std::endl; } // binds with lvalue parameter
T
Outlines void h(Data&&) { std::cout << "Data&& in h" << std::endl; } // binds with rvalue parameter
Universal
P
References template<typename T1, typename T2>
Recap void f(T1&& p1, T2&& p2) { // universal ref. gets lvalue or rvalue from arg by template type deduction
T&& g(std::forward<T1>(p1)); // std::forward forwards lvalue arg to lvalue param and
N
auto
Rvalue vs. Universal
h(std::forward<T2>(p2)); // rvalue arg to rvalue param
References }
Perfect
int main() { int i { 0 }; Data d;
Forwarding f(i, d); // (lvalue, lvalue) binds with int& in g; Data& in h
Type Safety f(std::move(i), d); // (rvalue, lvalue) binds with int&& in g; Data& in h
Practice Examples f(i, std::move(d)); // (lvalue, rvalue) binds with int& in g; Data&& in h
std::forward
f(std::move(i), std::move(d)); // (rvalue, rvalue) binds with int&& in g; Data&& in h
}
Move is an
Optimization of • Lvalue arg passed to p1 ⇒ g(const int&) receives Lvalue
Copy
Compiler Generated
• Rvalue arg passed to p1 ⇒ g(int&&) receives Rvalue
Move • Lvalue arg passed to p2 ⇒ h(const Data&) receives Lvalue
Module Summary • Rvalue arg passed to p2 ⇒ h(Data&&) receives Rvalue
Programming in Modern C++ Partha Pratim Das M51.15
Perfect Forwarding
Module M51
L
Partha Pratim
Das
• Type compatibility verified upon instantiation
E
Weekly Recap
Objectives &
◦ Only int-compatible types valid for call to g()
T
Outlines ◦ Only Data-compatible types valid for call to h(). For example in the context of
Universal
...
P
References
Recap class DerivedData: public Data { public: DerivedData(): Data() { } };
T&& ...
N
auto
Rvalue vs. Universal
int main() { ... DerivedData d; ... }
References
Perfect
Forwarding The code works exactly as before. Whereas for
Type Safety
...
Practice Examples
class OtherData { int i; public: OtherData(): i(0) { } }; // another UDT
std::forward
...
Move is an
Optimization of
int main() { ... OtherData d; ... }
Copy
Compiler Generated
Move The code fails compilation: error: no matching function for call to h(OtherData&)
Module Summary
Programming in Modern C++ Partha Pratim Das M51.16
Perfect Forwarding
Module M51
• The flexibility can be removed via static assert (Module 48) as follows:
L
Partha Pratim
Das
E
Weekly Recap ...
Objectives & template<typename T1, typename T2>
T
Outlines void f(T1&& p1, T2&& p2) {
Universal // Asserts that T2 must be of type Data
P
References
static_assert(std::is_same< typename std::decay<T2>::type, Data >::value,
Recap
T&& "T2 must be Data");
N
auto
Rvalue vs. Universal
References g(std::forward<T1>(p1)); // T1 too may be asserted, if needed
Perfect
h(std::forward<T2>(p2));
Forwarding }
Type Safety
...
Practice Examples
class DerivedData: public Data { public: DerivedData(): Data() { } };
std::forward
...
Move is an
Optimization of
int main() { ... DerivedData d; ... }
Copy
Compiler Generated
Move Compile-time error: error: static assertion failed: T2 must be Data
Module Summary
Programming in Modern C++ Partha Pratim Das M51.17
Perfect Forwarding Example 1: Modified from slide M51.14
Module M51
#include <iostream>
class Data { int i; public: Data(int i = 5): i(i) { }
L
Partha Pratim
Das operator int() { return i; } // cast to int
Data& operator++() { ++i; return *this; } // pre-increment operator
E
Weekly Recap Data& operator--() { --i; return *this; } // pre-decrement operator
Objectives & };
T
Outlines void g(int& a) { std::cout << "int& in g: " << ++a << "; "; } // binds non-const lvalue param
Universal void g(int&& a) { std::cout << "int&& in g: " << --a << "; "; } // binds rvalue param
P
References
Recap void h(Data& a) { std::cout << "Data& in h: " << ++a << std::endl; } // binds non-const lvalue param
T&& void h(Data&& a) { std::cout << "Data&& in h: " << --a << std::endl; } // binds rvalue param
N
auto
Rvalue vs. Universal
References template<typename T1, typename T2> Without std::forward
void f(T1&& p1, T2&& p2) { int& in g: 1; Data& in h: 6
Perfect
Forwarding g(...); // called on p1 with or without std::forward int& in g: 6; Data& in h: 7
Type Safety h(...); // called on p1 with or without std::forward int& in g: 2; Data& in h: 8
Practice Examples } int& in g: 6; Data& in h: 8
std::forward
int main() { int i { 0 }; Data d;
f(i, d); // (lvalue, lvalue) With std::forward
Move is an f(5, d); // (rvalue, lvalue)
Optimization of int& in g: 1; Data& in h: 6
Copy f(i, Data(7)); // (lvalue, rvalue) int&& in g: 4; Data& in h: 7
Compiler Generated f(5, Data(7)); // (rvalue, rvalue) int& in g: 2; Data&& in h: 6
Move } int&& in g: 4; Data&& in h: 6
Module Summary
Programming in Modern C++ Partha Pratim Das M51.18
Perfect Forwarding Example 2: Generic Factory Method/1
Module M51
• Let us write a generic factory method that should be able to create each arbitrary object. That
means that the function should have the following characteristics:
L
Partha Pratim
Das
◦ Can take an arbitrary number of arguments
E
Weekly Recap
◦ Can accept lvalues and rvalues as an argument
Objectives &
◦ Forwards it arguments identical to the underlying constructor
T
Outlines
Universal
P
References #include <iostream>
Recap
T&&
template <typename T, typename Arg> // For efficiency reasons, the function template should
N
auto
Rvalue vs. Universal
T CreateObject(Arg& a) { // take its arguments by a non-const lvalue reference
References return T(a);
Perfect }
Forwarding int main() {
Type Safety int five=5; // lvalues
Practice Examples int myFive= CreateObject<int>(five);
std::forward std::cout << "myFive: " << myFive << std::endl;
Move is an
Optimization of
int myFive2= CreateObject<int>(5); // rvalues: error: cannot bind non-const lvalue reference
Copy // of type int& to an rvalue of type int
Compiler Generated std::cout << "myFive2: " << myFive2 << std::endl;
Move
}
Module Summary
Programming in Modern C++ Partha Pratim Das M51.19
Perfect Forwarding Example 2: Generic Factory Method/2
Module M51
L
Partha Pratim
Das
◦ Change the non-const lvalue reference to a const lvalue reference (that can bind an rvalue).
E
Weekly Recap
But that is not perfect, because we cannot change the function argument, if needed
Objectives &
◦ Overload the function template for a const lvalue reference and a non-const lvalue
T
Outlines
P
References
Recap
#include <iostream>
T&&
template <typename T, typename Arg> T CreateObject(Arg& a) { return T(a); }
N
auto // binds lvalues
Rvalue vs. Universal
References
template <typename T, typename Arg> T CreateObject(const Arg& a) { return T(a); } // binds rvalues
Perfect
Forwarding int main() {
Type Safety
int five = 5; // lvalues
Practice Examples
int myFive = CreateObject<int>(five);
std::forward std::cout << "myFive: " << myFive << std::endl; // myFive: 5
Move is an
Optimization of int myFive2 = CreateObject<int>(5); // rvalues
Copy std::cout << "myFive2: " << myFive2 << std::endl; // myFive2: 5
Compiler Generated
Move
}
Module Summary
Programming in Modern C++ Partha Pratim Das M51.20
Perfect Forwarding Example 2: Generic Factory Method/3
Module M51
• The solution has two conceptual issues:
L
Partha Pratim
Das ◦ To support n arguments, we need to overload 2n + 1 variations of CreateObject<T>(...).
”+1” for the function CreateObject<T>() without any argument
E
Weekly Recap
Objectives &
◦ Without the overload, the forwarding problem would appear for rvalue arguments as they
T
Outlines will be copied instead of being moved
Universal
• So we need to use universal reference in CreateObject<T>(...) with std::forward
P
References
Recap
#include <iostream>
T&&
N
auto
Rvalue vs. Universal template <typename T, typename Arg>
References T CreateObject(Arg&& a) { // Universal reference
Perfect return T(std::forward<Arg>(a)); // std::forward
Forwarding }
Type Safety int main() {
Practice Examples int five = 5; // lvalues
std::forward int myFive = CreateObject<int>(five);
std::cout << "myFive: " << myFive << std::endl; // myFive: 5
Move is an
Optimization of
Copy int myFive2 = CreateObject<int>(5); // rvalues
Compiler Generated std::cout << "myFive2: " << myFive2 << std::endl; // myFive2: 5
Move
}
Module Summary
Programming in Modern C++ Partha Pratim Das M51.21
Perfect Forwarding Example 2: Generic Factory Method/4
Module M51 • CreateObject<T>() needs exactly one argument perfectly forwarded to the constructor
• For arbitrary number of arguments, we need a variadic template (TBD later)
L
Partha Pratim
Das
#include <iostream>
#include <string>
E
Weekly Recap
#include <utility>
Objectives & template <typename T, typename ... Args> // Variadic Templates can get an arbitrary number of arguments
T
Outlines
T CreateObject(Args&& ... args) { return T(std::forward<Args>(args)...); }
Universal int main() {
P
References
int five = 5, myFive = CreateObject<int>(five); // lvalues
Recap
std::cout << "myFive: " << myFive << std::endl; // myFive: 5
T&&
std::string str { "Lvalue" }, str2 = CreateObject<std::string>(str);
N
auto
Rvalue vs. Universal std::cout << "str2: " << str2 << std::endl; // str2: Lvalue
References
Module M51 • Let us design an apply functor that would take a function and its arguments and apply the
function on the arguments
L
Partha Pratim
Das template<typename F, typename... Ts> // Using variadic template (TBD later)
auto apply(std::ostream& os, F&& func, Ts&&... args)
E
Weekly Recap -> decltype(func(args...)) { // may not preserves rvalue-ness
Objectives & os << "Forwarding:: ";
T
Outlines return func(args...); // may not preserves rvalue-ness
Universal }
P
References
Recap
• args... are lvalues, but apply’s caller may have passed rvalues:
T&& ◦ Templates can distinguish rvalues from lvalues
N
auto
Rvalue vs. Universal ◦ apply might call the wrong overload of func
References
class Data { };
Perfect Data myData() { return Data(); }
Forwarding
Type Safety
class DataDispatcher { public:
Practice Examples
void operator()(const Data&) { std::cout << "operator()(const Data&) called\n\n"; } // takes lvalue
std::forward void operator()(Data&&) { std::cout << "operator()(Data&&) called\n\n"; } // takes rvalue
Move is an };
Optimization of int main() { Data d = myData();
Copy apply(std::cout, DataDispatcher(), d); // Forwarding:: operator()(const Data&) called
Compiler Generated
Move
apply(std::cout, DataDispatcher(), myData()); // Forwarding:: operator()(const Data&) called
// rvalue forwarded as lvalue!
Module Summary
}
Programming in Modern C++ Partha Pratim Das M51.23
Perfect Forwarding Example 3: apply Functor/2
Module M51
• Naturally, perfect forwarding solves
L
Partha Pratim
Das
template<typename F, typename... Ts>
E
Weekly Recap auto apply(std::ostream& os, F&& func, Ts&&... args) // return type is same as func’s on original args
Objectives & -> decltype(func(std::forward<Ts>(args)...)) { // preserves lvalue-ness / rvalue-ness
T
Outlines os << "Forwarding:: ";
Universal
return func(std::forward<Ts>(args)...); // preserves lvalue-ness / rvalue-ness
}
P
References
Recap ...
T&& int main() { Data d = myData();
N
auto apply(std::cout, DataDispatcher(), d); // Forwarding:: operator()(const Data&) called
Rvalue vs. Universal
References
apply(std::cout, DataDispatcher(), myData()); // Forwarding:: operator()(Data&&) called
Perfect
Forwarding
}
Type Safety
Practice Examples
• With return type deduction [C++14]
std::forward
template<typename F, typename... Ts>
Move is an
Optimization of
decltype(auto) apply(std::ostream& os, F&& func, Ts&&... args) { // return type deduction
Copy os << "Forwarding:: ";
Compiler Generated return func(std::forward<Ts>(args)...);
Move
}
Module Summary
Programming in Modern C++ Partha Pratim Das M51.24
Perfect Forwarding Example 3: apply Functor/3
Module M51 • Perfect forwarding works perfectly with mixed bindings as well
#include <iostream>
L
Partha Pratim
Das using namespace std;
class Data { };
E
Weekly Recap
Data myData() { return Data(); }
Objectives &
T
Outlines class DataDispatcher { public: // mixed binding for two parameters
Universal void operator()(const Data&, const Data&){ cout<< "operator()(const Data&, const Data&) called\n\n"; }
P
References void operator()(const Data&, Data&&){ cout<< "operator()(const Data&, Data&&) called\n\n"; }
Recap void operator()(Data&&, const Data&){ cout<< "operator()(Data&&, const Data&) called\n\n"; }
T&& void operator()(Data&&, Data&&){ cout<< "operator()(Data&&, Data&&) called\n\n"; }
N
auto
Rvalue vs. Universal
};
References template<typename F, typename... Ts>
Perfect
auto apply(ostream& os, F&& func, Ts&&... args) -> decltype(func(forward<Ts>(args)...)) {
Forwarding return func(forward<Ts>(args)...);
Type Safety }
Practice Examples int main() {
std::forward
Data d = myData();
apply(cout, DataDispatcher(), d, d); // operator()(const Data&, const Data&) called
Move is an apply(cout, DataDispatcher(), d, myData()); // operator()(const Data&, Data&&) called
Optimization of
Copy apply(cout, DataDispatcher(), myData(), d); // operator()(Data&&, const Data&) called
Compiler Generated apply(cout, DataDispatcher(), myData(), myData()); // operator()(Data&&, Data&&) called
Move }
Module Summary
Programming in Modern C++ Partha Pratim Das M51.25
std::forward
Module M51
Sources:
•
L
Partha Pratim
Das
Universal References in C++11 – Scott Meyers, isocpp.org, 2012
• std::forward, cppreference.com
• Quick Q: What’s the difference between std::move and std::forward?, isocpp.org
E
Weekly Recap
• An Overview of the New C++ (C++11/14), Scott Meyers Training Courses
Objectives &
• Scott Meyers on C++
T
Outlines
Universal
P
References
Recap
T&&
N
auto
Rvalue vs. Universal
References
Perfect
Forwarding
Type Safety
Practice Examples
std::forward
Move is an
std::forward
Optimization of
Copy
Compiler Generated
Move
Module Summary
Programming in Modern C++ Partha Pratim Das M51.26
std::forward
Module M51
L
Partha Pratim
Das
template<typename T1, typename T2>
E
Weekly Recap
void f(T1&& p1, T2&& p2) { ... h(std::forward<T2>(p2)); }
Objectives &
T
Outlines ◦ T a reference (that is,T is T&) ⇒ lvalue was passed to p2
Universal
. std::forward<T>(p2) should return lvalue
P
References
Recap
T&&
◦ T a non-reference (that is, T is T) ⇒ rvalue was passed to p2
N
auto
Rvalue vs. Universal
. std::forward<T>(p2) should return rvalue
References
Perfect
• std::forward is provided in <utility> for this
Forwarding
Type Safety
◦ Applicable only to function templates
Practice Examples
◦ Preserves arguments’ lvalue-ness / rvalue-ness / const-ness when forwarding them
std::forward
to other functions
Move is an
Optimization of
Copy
• Let us take a look at the implementation
Compiler Generated
Move
Module Summary
Programming in Modern C++ Partha Pratim Das M51.27
std::forward
Module M51
• C++11 implementations:
L
Partha Pratim
Das
template<typename T> // For lvalues (T is T&);
E
Weekly Recap T&& // return lvalue reference
Objectives & forward(typename remove_reference<T>::type& t) noexcept
T
Outlines { return static_cast<T&&>(t); }
Universal
P
References
Recap
template<typename T> // For rvalues (T is T);
T&& T&& // return rvalue reference
N
auto forward(typename remove_reference<T>::type&& t) noexcept
Rvalue vs. Universal
References { return static_cast<T&&>(t); }
Perfect
Forwarding
Type Safety
• By design, param type disables type deduction ⇒ callers must specify T:
Practice Examples
std::forward template<typename T1, typename T2> void f(T1&& p1, T2&& p2)
Move is an { g(std::forward(p1)); ... } // error! Cannot deduce T1 in call to std::forward
Optimization of
Copy
Compiler Generated template<typename T1, typename T2> void f(T1&& p1, T2&& p2)
Move
{ g(std::forward<T1>(p1)); ... } // fine
Module Summary
Programming in Modern C++ Partha Pratim Das M51.28
Move is an Optimization of Copy
Module M51
L
Partha Pratim
Das
E
Weekly Recap
Objectives &
T
Outlines
Universal
P
References
Recap
T&&
N
auto
Rvalue vs. Universal
References
Perfect
Forwarding
Type Safety
Practice Examples
std::forward
Move is an Optimization of Copy
Move is an
Optimization of Sources:
Copy
• Scott Meyers on C++
Compiler Generated
Move • An Overview of the New C++ (C++11/14), Scott Meyers Training Courses
Module Summary
Programming in Modern C++ Partha Pratim Das M51.29
Move is an Optimization of Copy
Module M51
Copy Only Copy & Move
L
Partha Pratim
Das • Move requests for copyable types w/o • If MyResource adds move support:
move support yield copies:
E
Weekly Recap
Objectives & class MyResource { public: // w/o move support class MyResource { public: // with move support
T
Outlines MyResource(const MyResource&); // copy ctor MyResource(const MyResource&); // copy ctor
Universal MyResource(MyResource&&) noexcept; // move ctor
};
P
References };
Recap class MyClass { public: // with move support class MyClass { public: // with move support
T&& MyClass(MyClass&& src) // move ctor MyClass(MyClass&& src) noexcept
N
auto // request to move r’s value
// request to move r’s value
Rvalue vs. Universal
References : w(std::move(src.r)) { ... } : w(std::move(src.r)) { ... }
private: MyResource r; // no move support private: MyResource r;
Perfect
Forwarding }; };
Type Safety
Practice Examples
src.r is copied to r: src.r is moved to r:
std::forward
Move is an
• std::move(src.r) returns an rvalue of type • std::move(src.r) returns an rvalue of type
Optimization of MyResource MyResource
Copy
• That rvalue is passed to MyResource’s copy • That rvalue is passed to MyResource’s move
Compiler Generated
Move constructor constructor via normal overloading resolution
Module Summary
Programming in Modern C++ Partha Pratim Das M51.30
Move is an Optimization of Copy
Module M51
• Implications:
L
Partha Pratim
Das ◦ Giving classes move support can improve performance even for move-unaware code
. Copy requests for rvalues may silently become moves
E
Weekly Recap
Objectives & ◦ Move requests safe for types without explicit move support
T
Outlines
P
References
Recap − For example, all built-in types (POD)
T&&
◦ Move support may exist even if copy operations do not
N
auto
Rvalue vs. Universal
References . For example, Move-only types like std::thread and std::unique ptr that
Perfect
Forwarding
are moveable, but not copyable
Type Safety
◦ Types should support move when moving cheaper than copying
Practice Examples
L
Partha Pratim
Das class MyList { public:
...
E
Weekly Recap
void setID(const std::string& newId) // copy param
Objectives & { id = newId; }
T
Outlines
void setID(std::string&& newId) noexcept // move param
Universal { id = std::move(newId); }
P
References void setVals(const std::vector<int>& newVals) // copy param
Recap
{ vals = newVals; }
T&&
void setVals(std::vector<int>&& newVals) // move param
N
auto
Rvalue vs. Universal
{ vals = std::move(newVals); }
References ...
Perfect private:
Forwarding std::string id;
Type Safety std::vector<int> vals;
Practice Examples };
std::forward
• Note:
Move is an
Optimization of ◦ As the move operator= of std::string is noexcept, setId is declared noexcept
Copy
Compiler Generated
◦ Whereas setVals is not declared noexcept, as the move operator= of std::vector is
Move
not declared noexcept
Module Summary
Programming in Modern C++ Partha Pratim Das M51.32
Compiler Generated Move Operations
L
Partha Pratim
Das
• Conditions:
E
Weekly Recap
Objectives &
◦ All data members and base classes are movable
T
Outlines
. Implicit move operations move everything
Universal
. Most types qualify:
P
References
Recap
T&& − All built-in types (move ≡ copy).
N
auto
Rvalue vs. Universal
− Most standard library types (for example, all containers).
References
Perfect
◦ Generated operations likely to maintain class invariants
Forwarding
Type Safety
. No user-declared copy or move operations
Practice Examples
− Custom semantics for any ⇒ default semantics inappropriate
std::forward
− Move is an optimization of copy
Move is an
Optimization of
Copy
. No user-declared destructor
Compiler Generated
Move
− Often indicates presence of implicit class invariant
Module Summary • More on this later in the Module discussing default and delete
Programming in Modern C++ Partha Pratim Das M51.33
Compiler Generated Move Operations:
Custom Deletion ⇒ Custom Copying
Module M51
class Widget { private:
L
Partha Pratim
Das std::vector<int> v;
std::set<double> s;
E
Weekly Recap
std::size_t sizeSum;
Objectives &
public:
T
Outlines
~Widget() { assert(sizeSum == v.size()+s.size()); }
Universal
...
P
References
Recap };
T&&
N
auto
Rvalue vs. Universal
References
• If Widget had implicitly-generated move operations:
Perfect std::vector<Widget> vw;
Forwarding
Type Safety
Widget w;
Practice Examples ... // put stuff in w’s containers
std::forward vw.push_back(std::move(w)); // move w into vw
Move is an
... // no use of w
Optimization of
Copy • User-declared dtor ⇒ no compiler-generated move ops for Widget
Compiler Generated
Move
Module Summary
Programming in Modern C++ Partha Pratim Das M51.34
Compiler Generated Move Operations:
Custom Moving ⇒ Custom Copying
Module M51 copyable & movable type copyable type; not movable
L
Partha Pratim
Das
std::u16string name; // copyable/movable type std::u16string name;
long long value; // copyable/movable type long long value;
E
Weekly Recap
public: explicit Widget1(std::u16string n); public: explicit Widget2(std::u16string n);
Objectives & // user-declared copy ctor
T
Outlines
Widget2(const Widget2& rhs);
Universal }; // implicit copy/move ctor }; // => no implicit move ops
P
References // implicit copy/move operator= // implicit copy operator=
Recap
T&& • Declaring a move operation prevents generation of copy operations
N
auto
Rvalue vs. Universal ◦ Custom move semantics ⇒ custom copy semantics
References
Perfect
. Move is an optimization of copy
Forwarding
class Widget3 { private: // movable type; not copyable
Type Safety
std::u16string name;
Practice Examples
long long value;
std::forward public:
Move is an explicit Widget3(std::u16string n);
Optimization of Widget3(Widget3&& rhs) noexcept; // user-declared move ctor => no implicit copy ops
Copy Widget3& // user-declared move op=
Compiler Generated
Move
operator=(Widget3&& rhs) noexcept; // => no implicit copy ops
};
Module Summary
Programming in Modern C++ Partha Pratim Das M51.35
Module Summary
Module M51
• Learnt how Rvalue Reference works as a Universal Reference under template type
L
Partha Pratim
Das
deduction
E
Weekly Recap
• Understood the problem of forwarding of parameters under template type deduction
Objectives &
and its solution using Universal Reference and std::forward
T
Outlines
Universal
• Learnt the implementation of std::forward
P
References
Recap
T&& • Understood how Move works as an optimization of Copy
N
auto
Rvalue vs. Universal
References
Perfect
Forwarding
Type Safety
Practice Examples
std::forward
Move is an
Optimization of
Copy
Compiler Generated
Move
Module Summary
Programming in Modern C++ Partha Pratim Das M51.36