Upload
marshall-price
View
216
Download
0
Tags:
Embed Size (px)
Citation preview
Unrestricted © Siemens AG 2014 All rights reserved. Smarter decisions, better products.
Move semantics, rvalue references && perfect forwarding, part 1Bert Rodiers, Software Architect @ Siemens Industry Software NV
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 2 Siemens PLM Software
Some questions
• Who has some knowledge about move semantics?• Knows why we have move semantics?• Is comfortable with move semantics?• Knows how to add move support to an existing class?• Has added move support to an existing class?• Knows what std::move does?• Knows about rvalue references?• Knows about ref-qualifiers?
• void memberFunction() && ;• void memberFunction() & ;
• Knows what perfect forwarding is?• Knows about universal references?• Knows about forwarding references?• Reference collapsing rules?• Knows what std::forward does?
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 3 Siemens PLM Software
Move Semantics
• What?• Why?
Þ Some examples
• Broad topic• Presentation in two parts
• First part mostly about move semantics and rvalue references + related topics
• Second part mostly about “forwarding references” and perfect forwarding + related topics
• Some things not discussed• Content reduced (21 hidden slides in part 1)
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 4 Siemens PLM Software
Move semantics - Example 1
class MyString{public: MyString(const char* string) : string_(string) {}private: std::string string_;};
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 5 Siemens PLM Software
Move semantics - Example 1
For loop takes 3317 ms(1424 ms with reserve)
std::vector<MyString> vector;//vector.reserve(count);
for (int i = 0; i < count; ++i){ vector.push_back(MyString("To be, or not to be, that is the question: Whether…"));}
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 6 Siemens PLM Software
Move semantics - Example 1
• This will copy MyString (typically new heap allocation)• What if we could avoid this copy (move semantics):
• Why move semantics?=> Performance (avoid unnecessary copies)
Mostly done automatically=> Explicitly transfer ownership
Example: std::unique_ptr
vector.push_back(MyString("To be, or not to be, that is the question: Whether…"));
No move support Move support
no reserve 3317 ms 900 ms
reserve 1424 ms 728 ms
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 17 Siemens PLM Software
Move Support – Why? Performance!
• (Temporary) objects often copied• Examples
• Returned by value ( RVO/NRVO)
• Copied when put in a member
• Reallocation of memory
• If reallocation => Copying all values to new location• Also with other functions: erase, resize, insert, push_front, …
• Move: take over internal representation/state: avoid allocations, …
myVec = CreateVec();
MyString(const std::string& string) : string_(string) {}
std::vector<MyString> vec;…vec.push_back("5");
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 18 Siemens PLM Software
When moved?
• Class supports it• Explicit support• Generated support
• OK to move• Temporary objects• Explicit in code (std::move)
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 19 Siemens PLM Software
Example: Explicitly moving in code
• std::swap (presume T supports moving)
template<class T>void swap(T& left, T& right) { T temp(left); // Copy left to temp left = right; // Copy right to left right = temp; // Copy temp to right} // Destroy temp
template<class T>void swap(T& left, T& right) { T temp(std::move(left)); // Move left to temp left = std::move(right); // Move right to left right = std::move(temp); // Move temp to right} // Destroy temp (probably has no “real” state anymore)
std::move facilitates movesStill 31 slides to go for a detailed
explanation
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 20 Siemens PLM Software
std::swap example for std::vector
Initial situation
buffer_size_ = 6Left:
buffer_size_ = 5Right:
template<class T>void swap(T& left, T& right) { T temp(std::move(left)); // Move left to temp left = std::move(right); // Move right to left right = std::move(temp); // Move temp to right} // Destroy temp (probably has no “real” state anymore)
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 21 Siemens PLM Software
buffer_size_ = 5
std::swap example for std::vector
Temp:
T temp(std::move(left)); // Move left to temp
template<class T>void swap(T& left, T& right) { T temp(std::move(left)); // Move left to temp left = std::move(right); // Move right to left right = std::move(temp); // Move temp to right} // Destroy temp (probably has no “real” state anymore)
buffer_size_ = 0Left:
Right:
buffer_size_ = 6
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 22 Siemens PLM Software
std::swap example for std::vector
left = std::move(right); // Move right to left
template<class T>void swap(T& left, T& right) { T temp(std::move(left)); // Move left to temp left = std::move(right); // Move right to left right = std::move(temp); // Move temp to right} // Destroy temp (probably has no “real” state anymore)
buffer_size_ = 0
Temp:
buffer_size_ = 5Left:
Right:
buffer_size_ = 6
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 23 Siemens PLM Software
std::swap example for std::vector
right = std::move(temp); // Move temp to right
template<class T>void swap(T& left, T& right) { T temp(std::move(left)); // Move left to temp left = std::move(right); // Move right to left right = std::move(temp); // Move temp to right} // Destroy temp (probably has no “real” state anymore)
buffer_size_ = 6
Temp:
buffer_size_ = 5Left:
Right:
buffer_size_ = 0
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 24 Siemens PLM Software
std::swap example for std::vector
} // Destroy temp (probably has no “real” state anymore)Move itself not destructive
template<class T>void swap(T& left, T& right) { T temp(std::move(left)); // Move left to temp left = std::move(right); // Move right to left right = std::move(temp); // Move temp to right} // Destroy temp (probably has no “real” state anymore)
buffer_size_ = 6
Temp:
buffer_size_ = 5Left:
Right:
buffer_size_ = 0
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 25 Siemens PLM Software
Importance? What moved? Enabling move semantics?
Moving most important when• Copying expensive• Object has data on heap• Copying impossible: std::ofstream, std::unique_ptr
What moved?• Temporary variables• Other objects: using std::move• If class has move support
• Enabling move semantics• Based on rvalues and rvalue references• Move constructor & move assignment operator
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 26 Siemens PLM Software
Lvalues rvaluesLvalues references rvalues references
• Definition rather complicated (with lvalues, glvalues, rvalues, prvalues and xvalues)
• Workable in practice:• Variable has name => lvalue• Rvalues: temporary variables without a name
• Lvalue references: int&, const int&
• Rvalue references: int&&, const int&&
• Lvalues can’t bind with rvalues references
std::string a("test"); // a: lvalue
std::string& la = a; // la: lvalue reference to a
std::string f();f(); // f returns rvalue
const std::string&& rf = f(); // rf: const rvalue reference to rvalue
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 31 Siemens PLM Software
Lvalues rvalues – Binding of variables
• Note: Visual Studio accepts non-const rvalues for custom types (not standard compliant)
• Lvalues can only bind to lvalue references, not rvalue references
• Rvalues can bind to rvalue references and const lvalue references
• by default to rvalue references
Argument type:Function signature
lvalue const lvalue rvalue const rvalue
f(const std::string&)
OK OK OK OK
f(const std::string&&)
NOK NOK OK OK
f(std::string&) OK NOK NOK (*) NOK
f(std::string&&) NOK NOK OK NOK
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 35 Siemens PLM Software
Lvalues rvalues
• We want to move temporaries (rvalues)• And nothing else but rvalues
• Rvalue references identify what can be movedÞ Overload “copy constructor” and = for non-const rvalue
references(const == don’t change me)
Argument type: lvalue const lvalue rvalue const rvalue
f(const std::string&)
OK OK OK OK
f(const std::string&&)
NOK NOK OK OK
f(std::string&) OK NOK NOK (*) NOK
f(std::string&&) NOK NOK OK NOK
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 36 Siemens PLM Software
Add move support in class
• Add• Move constructor• Move assignment operator
• noexcept • Supported from Visual Studio 2015• Some STL functions don’t accept throwing move
operations
MyString(const MyString&);MyString& operator=(const MyString&);MyString(MyString&&) noexcept;MyString& operator=(MyString&&) noexcept;
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 37 Siemens PLM Software
Implementing Move Semantics
MyString (MyString&& string) noexcept : string_(std::move(string.string_)){}
MyString& operator=(MyString&& string) noexcept{ string_ = std::move(string.string_); return *this;}
std::move facilitates movesStill 18 slides to go for a detailed
explanation
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 38 Siemens PLM Software
Implementing Move Semantics - Note
• Moved object has to remain in a (unspecified) valid state (§17.6.5.15 SP: ~, =)
• string is an lvalue (it has a name)• std::move has to be called!
• Even with std::move: might still copy (move operations not (or not correctly) implemented)
MyString (MyString&& string)
MyString (MyString&& string) : string_(std::move(string.string_)){}MyString& operator=(MyString&& string) { string_ = std::move(string.string_); return *this;}
Without std::move => Copy!
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 39 Siemens PLM Software
noexcept
• C++98: void f() throw();• C++11: void f() noexcept;
• throw()• Exception thrown:
• Unwind stack to caller of f• Call std::unexpected
• By default eventually calls std::terminate• noexcept
• May unwind stack• Call std::terminate
• Noexcept => more optimization opportunities
Note: Microsoft doesn’t implement throw() correctly:• Assume no exceptions• If exception: undefined behavior
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 40 Siemens PLM Software
Exception safety guaranties
• Basic exception safety guaranteeInvariants remain valid (no corruption, no leaks, …)
• Strong exception safety guaranteeState not changed
• C++ Standard requires Strong exception safety guarantee for many functions
• Examples: std::vector::push_back, std::vector::resize
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 41 Siemens PLM Software
Behavior of std::vector::push_back in C++98
1. if Size == capacity: new buffer2. Copy elements from existing buffer3. Copy new element4. Delete old buffer
• If exception during (1, 2 or 3) => original buffer not changed• Strong exception safety guarantee
Copy
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 42 Siemens PLM Software
Erroneous behavior of std::vector::push_back in C++11
1. if Size == capacity: new buffer2. Move elements from existing buffer3. Move/copy new element4. Delete old buffer
• If exception during 2 or 3 => original buffer changed (elements already moved!)
• Regression w.r.t. C++98
Move
Implementations shouldn’t unconditionally move!
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 43 Siemens PLM Software
Behavior of std::vector::push_back in C++11
std::vector::push_back• Doesn’t call std::move• But std::move_if_noexcept
• Only moved if std::move doesn’t throw• Make move constructor/move assignment operator
noexcept (if possible)• Not copy-constructible => still moved (no regression)
std::move facilitates movesStill 12 slides to go for a detailed
explanation
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 44 Siemens PLM Software
Implementing Move Semantics
class CMyIntVector{public:…private: int* data_; unsigned int size_;};
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 47 Siemens PLM Software
Implementing Move Semantics
CMyIntVector(CMyIntVector&& vector) noexcept : data_(vector.data_) , size_(vector.size_){ vector.data_ = nullptr; vector.size_ = 0;}
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 48 Siemens PLM Software
Implementing Move Semantics
CMyIntVector& operator=(CMyIntVector&& vector) noexcept { assert(&vector != this); // Alternative: If-test
// Clean-up memory delete[] data_;
// Fill-in members size_ = vector.size_; data_ = vector.data_;
// Make vector an “empty”-vector vector.size_ = 0; vector.data_ = nullptr; return *this;}
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 49 Siemens PLM Software
Alternative implementations for move operations
• std::swap to exchange members• Call move assignment operator in move constructor• Pass by value in assignment operator• Copy/swap idiom
• Moved to part 2
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 50 Siemens PLM Software
Move semantics – Optimal implementation
class MyString{public: MyString(const char* string) : string_(string) {}private: std::string string_;};
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 51 Siemens PLM Software
Generated Move Constructor/Assignment Operator
• Good news:• Move Constructor/Assignment Operator generated by compiler
• But only when • No user-declared copy constructor• No user-declared copy assignment operator• No user-declared move assignment operator/move constructor• No user-declared destructor
• Supported from Visual Studio 2015 (not a typo )
• Rule of five/zero
• = default• MyString& operator=(MyString&& vector) = default;• Noexcept
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 52 Siemens PLM Software
std::move
• Converts lvalues into rvalues references• A simple cast• Doesn’t move anything
• A a; a = std::move(b);• = does the move
• std::move doesn't necessarily lead to a move operation• std::move (const object)• Class might not implement move operations• Move operations possibly not generated
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 53 Siemens PLM Software
Something wrong?
MyStringVector getDataTrue() { MyStringVector result; … return std::move(result); // move to avoid copy}
MyStringVector getDataFalse() { return std::move(getReverseData()); // move to avoid copy}
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 54 Siemens PLM Software
Don’t return std::move(…)
MyStringVector getDataTrue() { MyStringVector result; … return result;}
MyStringVector getDataFalse() { return getReverseData();}
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 55 Siemens PLM Software
Don’t return std::move(…)
Move not necessary (*)
• Move is done implicit if local values are returned
RVO and NRVO can’t be applied with std::move (See hidden slides )• Not a named object• Not (obviously) a temporary
* There are a limited number of advanced use-cases where it is useful to return with std::move. The default hower should be not to return with std::move
MyStringVector getDataTrue() { MyStringVector result; …return std::move(result);}
MyStringVector getDataFalse() {return std::move(getReverseData());}
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 56 Siemens PLM Software
Something wrong?
const MyStringVector getData(){ MyStringVector result; … return result;}
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 57 Siemens PLM Software
Don’t return by const value
• Const == don’t change me• Can’t be moved• Compiles, but copy instead of move
MyStringVector& operator=(const MyStringVector& vector)
MyStringVector& operator=(MyStringVector&& vector)
const MyStringVector getData(){ MyStringVector result; … return result;}
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 58 Siemens PLM Software
Something wrong?
std::string a(“Test”)std::string b = std::move(a);…a += “test”;
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 59 Siemens PLM Software
Don't use a value that has been moved
• Object in unspecified valid state
std::string a(“Test”)std::string b = std::move(a);…a += “test”;
a might be anything:“Test”, “”, “Invalid String”, …
Most likely: “”Unspecified, but valid
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 60 Siemens PLM Software
Move semantics && rvalue references
• Broad topic• Hidden slides
• More examples for move semantics• RVO + NRVO: examples + explanation• More examples for lvalues and rvalues (conversion rules)• …
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 61 Siemens PLM Software
Move semantics && rvalue references
• Part 21. Alternative implementations for move operations2. Moving into C++14 lambda expressions3. Rvalue references and templates (different binding rules)
• void f(int&& a);=> Only rvalues can bind with a
• template <typename T> void f(T&& a);=> Lvalues can bind with a
4. Reference Collapsing rules Forwarding references5. Perfect forwarding6. std::forward7. How to pass arguments8. Emplace
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 62 Siemens PLM Software
Move semantics: Take-away notes
• Most (application) developers don’t need to be able to write move assignment operators and move constructors
• Generated• Implemented for STL data structures• Not a bottleneck for most types• C++98 still good default
• Know existence of std::move, some idea what it does, when to use it• Cast to rvalue reference• Necessary for std::unique_ptr
2014-12-17
Unrestricted © Siemens AG 2014 All rights reserved.
Page 63 Siemens PLM Software
Move semantics: Take-away notes
• Tips• Don’t return with std::move(…)• Don’t use a moved value• Function Signature
• Either return• const T& (not for objects on stack)• T& (not for objects on stack)• T
• Not: const T, T&& or const T&&