Move semantics && rvalue references, part 1″ by Bert Rodiers

Report
Move semantics, rvalue
references && perfect forwarding,
part 1
Bert Rodiers, Software Architect @ Siemens Industry Software NV
Unrestricted © Siemens AG 2014 All rights reserved.
Smarter decisions, better products.
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?
Unrestricted © Siemens AG 2014 All rights reserved.
Page 2
2014-12-17
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)
Unrestricted © Siemens AG 2014 All rights reserved.
Page 3
2014-12-17
Siemens PLM Software
Move semantics - Example 1
class MyString
{
public:
MyString(const char* string)
: string_(string) {}
private:
std::string string_;
};
Unrestricted © Siemens AG 2014 All rights reserved.
Page 4
2014-12-17
Siemens PLM Software
Move semantics - Example 1
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…"));
}
For loop takes 3317 ms
(1424 ms with reserve)
Unrestricted © Siemens AG 2014 All rights reserved.
Page 5
2014-12-17
Siemens PLM Software
Move semantics - Example 1
vector.push_back(MyString("To be, or not to be,
that is the question: Whether…"));
• This will copy MyString (typically new heap allocation)
• What if we could avoid this copy (move semantics):
No move support
Move support
no reserve
3317 ms
900 ms
reserve
1424 ms
728 ms
• Why move semantics?
=> Performance (avoid unnecessary copies)
 Mostly done automatically
=> Explicitly transfer ownership
Example: std::unique_ptr
Unrestricted © Siemens AG 2014 All rights reserved.
Page 6
2014-12-17
Siemens PLM Software
Move Support – Why? Performance!
• (Temporary) objects often copied
• Examples
• Returned by value ( RVO/NRVO)
myVec = CreateVec();
• Copied when put in a member
MyString(const std::string& string) : string_(string) {}
• Reallocation of memory
std::vector<MyString> vec;
…
vec.push_back("5");
• 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, …
Unrestricted © Siemens AG 2014 All rights reserved.
Page 17
2014-12-17
Siemens PLM Software
When moved?
• Class supports it
• Explicit support
• Generated support
• OK to move
• Temporary objects
• Explicit in code (std::move)
Unrestricted © Siemens AG 2014 All rights reserved.
Page 18
2014-12-17
Siemens PLM Software
Example: Explicitly moving in code
• std::swap (presume T supports moving)
template<class T>
void swap(T& left,
T temp(left); //
left = right; //
right = temp; //
} // Destroy temp
T& right) {
Copy left to temp
Copy right to left
Copy temp to right
template<class T>
void swap(T& left, T& right) {
T temp(std::move(left)); // Move
left = std::move(right); // Move
right = std::move(temp); // Move
} // Destroy temp (probably has no
std::move facilitates moves
Still 31 slides to go for a detailed
explanation
left to temp
right to left
temp to right
“real” state anymore)
Unrestricted © Siemens AG 2014 All rights reserved.
Page 19
2014-12-17
Siemens PLM Software
std::swap example for std::vector
Initial situation
Left:
buffer_
size_ = 6
buffer_
Right: size_ = 5
template<class T>
void swap(T& left, T& right) {
T temp(std::move(left)); // Move
left = std::move(right); // Move
right = std::move(temp); // Move
} // Destroy temp (probably has no
anymore)
left to temp
right to left
temp to right
“real” state
Unrestricted © Siemens AG 2014 All rights reserved.
Page 20
2014-12-17
Siemens PLM Software
std::swap example for std::vector
T temp(std::move(left)); // Move left
Left:
to temp
buffer_
size_ = 0
buffer_
Right: size_ = 5
Temp: buffer_
size_ = 6
template<class T>
void swap(T& left, T& right) {
T temp(std::move(left)); // Move
left = std::move(right); // Move
right = std::move(temp); // Move
} // Destroy temp (probably has no
anymore)
left to temp
right to left
temp to right
“real” state
Unrestricted © Siemens AG 2014 All rights reserved.
Page 21
2014-12-17
Siemens PLM Software
std::swap example for std::vector
left = std::move(right); // Move right to left
Left:
buffer_
size_ = 5
buffer_
Right: size_ = 0
Temp: buffer_
size_ = 6
template<class T>
void swap(T& left, T& right) {
T temp(std::move(left)); // Move
left = std::move(right); // Move
right = std::move(temp); // Move
} // Destroy temp (probably has no
anymore)
left to temp
right to left
temp to right
“real” state
Unrestricted © Siemens AG 2014 All rights reserved.
Page 22
2014-12-17
Siemens PLM Software
std::swap example for std::vector
right = std::move(temp); // Move temp
Left:
to right
buffer_
size_ = 5
buffer_
Right: size_ = 6
Temp: buffer_
size_ = 0
template<class T>
void swap(T& left, T& right) {
T temp(std::move(left)); // Move
left = std::move(right); // Move
right = std::move(temp); // Move
} // Destroy temp (probably has no
anymore)
left to temp
right to left
temp to right
“real” state
Unrestricted © Siemens AG 2014 All rights reserved.
Page 23
2014-12-17
Siemens PLM Software
std::swap example for std::vector
} // Destroy temp (probably has no “real” state anymore)
Move itself not destructive
Left:
buffer_
size_ = 5
buffer_
Right: size_ = 6
Temp: buffer_
size_ = 0
template<class T>
void swap(T& left, T& right) {
T temp(std::move(left)); // Move
left = std::move(right); // Move
right = std::move(temp); // Move
} // Destroy temp (probably has no
anymore)
left to temp
right to left
temp to right
“real” state
Unrestricted © Siemens AG 2014 All rights reserved.
Page 24
2014-12-17
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
Unrestricted © Siemens AG 2014 All rights reserved.
Page 25
2014-12-17
Siemens PLM Software
Lvalues  rvalues
Lvalues 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&
std::string a("test"); // a: lvalue
std::string& la = a;
•
// la: lvalue reference to a
Rvalue references: int&&, const int&&
std::string f();
f(); // f returns rvalue
const std::string&& rf = f();
// rf: const rvalue reference to rvalue
•
Lvalues can’t bind with rvalues references
Unrestricted © Siemens AG 2014 All rights reserved.
Page 26
2014-12-17
Siemens PLM Software
Lvalues  rvalues – Binding of variables
Argument type: lvalue
Function signature
const lvalue
rvalue const rvalue
f(const std::string&)
OK
OK
OK
f(const std::string&&) NOK
NOK
OK
OK
f(std::string&)
OK
NOK
NOK (*) NOK
f(std::string&&)
NOK
NOK
OK
•
OK
NOK
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
Unrestricted © Siemens AG 2014 All rights reserved.
Page 31
2014-12-17
Siemens PLM Software
Lvalues  rvalues
• We want to move temporaries (rvalues)
• And nothing else but rvalues
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
• Rvalue references identify what can be moved
 Overload “copy constructor” and = for non-const rvalue
references
(const == don’t change me)
Unrestricted © Siemens AG 2014 All rights reserved.
Page 35
2014-12-17
Siemens PLM Software
Add move support in class
MyString(const MyString&);
MyString& operator=(const MyString&);
MyString(MyString&&) noexcept;
MyString& operator=(MyString&&) noexcept;
• Add
• Move constructor
• Move assignment operator
• noexcept
• Supported from Visual Studio 2015
• Some STL functions don’t accept throwing move operations
Unrestricted © Siemens AG 2014 All rights reserved.
Page 36
2014-12-17
Siemens PLM Software
Implementing Move Semantics
MyString (MyString&& string) noexcept :
string_(std::move(string.string_))
{
}
std::move facilitates moves
Still 18 slides to go for a detailed
explanation
MyString& operator=(MyString&& string) noexcept
{
string_ = std::move(string.string_);
return *this;
}
Unrestricted © Siemens AG 2014 All rights reserved.
Page 37
2014-12-17
Siemens PLM Software
Implementing Move Semantics - Note
• Moved object has to remain in a (unspecified) valid state (§17.6.5.15  SP: ~, =)
MyString (MyString&& string)
• string is an lvalue (it has a name)
• std::move has to be called!
MyString (MyString&& string)
: string_(std::move(string.string_))
{
Without std::move
}
MyString& operator=(MyString&& string)
{
string_ = std::move(string.string_);
return *this;
}
=> Copy!
• Even with std::move: might still copy (move operations not (or not correctly)
implemented)
Unrestricted © Siemens AG 2014 All rights reserved.
Page 38
2014-12-17
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
Unrestricted © Siemens AG 2014 All rights reserved.
Page 39
2014-12-17
Siemens PLM Software
Exception safety guaranties
• Basic exception safety guarantee
Invariants remain valid (no corruption, no leaks, …)
• Strong exception safety guarantee
State not changed
• C++ Standard requires Strong exception safety guarantee for
many functions
• Examples: std::vector::push_back, std::vector::resize
Unrestricted © Siemens AG 2014 All rights reserved.
Page 40
2014-12-17
Siemens PLM Software
Behavior of std::vector::push_back in C++98
Copy
1.
2.
3.
4.
if Size == capacity: new buffer
Copy elements from existing buffer
Copy new element
Delete old buffer
• If exception during (1, 2 or 3) => original buffer not changed
• Strong exception safety guarantee
Unrestricted © Siemens AG 2014 All rights reserved.
Page 41
2014-12-17
Siemens PLM Software
Erroneous behavior of std::vector::push_back in
C++11
Move
1.
2.
3.
4.
if Size == capacity: new buffer
Move elements from existing buffer
Move/copy new element
Delete old buffer
Implementations shouldn’t
unconditionally move!
• If exception during 2 or 3 => original buffer changed (elements
already moved!)
• Regression w.r.t. C++98
Unrestricted © Siemens AG 2014 All rights reserved.
Page 42
2014-12-17
Siemens PLM Software
Behavior of std::vector::push_back in C++11
std::vector::push_back
std::move facilitates moves
• Doesn’t call std::move
Still 12 slides to go for a detailed
• But std::move_if_noexcept
explanation
• Only moved if std::move doesn’t throw
• Make move constructor/move assignment operator
noexcept (if possible)
• Not copy-constructible => still moved (no regression)
Unrestricted © Siemens AG 2014 All rights reserved.
Page 43
2014-12-17
Siemens PLM Software
Implementing Move Semantics
class CMyIntVector
{
public:
…
private:
int* data_;
unsigned int size_;
};
Unrestricted © Siemens AG 2014 All rights reserved.
Page 44
2014-12-17
Siemens PLM Software
Implementing Move Semantics
CMyIntVector(CMyIntVector&& vector) noexcept
: data_(vector.data_)
, size_(vector.size_)
{
vector.data_ = nullptr;
vector.size_ = 0;
}
Unrestricted © Siemens AG 2014 All rights reserved.
Page 47
2014-12-17
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;
}
Unrestricted © Siemens AG 2014 All rights reserved.
Page 48
2014-12-17
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
Unrestricted © Siemens AG 2014 All rights reserved.
Page 49
2014-12-17
Siemens PLM Software
Move semantics – Optimal implementation
class MyString
{
public:
MyString(const char* string)
: string_(string) {}
private:
std::string string_;
};
Unrestricted © Siemens AG 2014 All rights reserved.
Page 50
2014-12-17
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
Unrestricted © Siemens AG 2014 All rights reserved.
Page 51
2014-12-17
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
Unrestricted © Siemens AG 2014 All rights reserved.
Page 52
2014-12-17
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
}
Unrestricted © Siemens AG 2014 All rights reserved.
Page 53
2014-12-17
Siemens PLM Software
Don’t return std::move(…)
MyStringVector getDataTrue() {
MyStringVector result;
…
return result;
}
MyStringVector getDataFalse() {
return getReverseData();
}
Unrestricted © Siemens AG 2014 All rights reserved.
Page 54
2014-12-17
Siemens PLM Software
Don’t return std::move(…)
MyStringVector getDataTrue() {
MyStringVector result;
…
return std::move(result);
}
MyStringVector getDataFalse() {
return std::move(getReverseData());
}
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
Unrestricted © Siemens AG 2014 All rights reserved.
Page 55
2014-12-17
Siemens PLM Software
Something wrong?
const MyStringVector getData()
{
MyStringVector result;
…
return result;
}
Unrestricted © Siemens AG 2014 All rights reserved.
Page 56
2014-12-17
Siemens PLM Software
Don’t return by const value
const MyStringVector getData()
{
MyStringVector result;
…
return result;
}
• Const == don’t change me
• Can’t be moved
• Compiles, but copy instead of move
MyStringVector& operator=(const MyStringVector& vector)
MyStringVector& operator=(MyStringVector&& vector)
Unrestricted © Siemens AG 2014 All rights reserved.
Page 57
2014-12-17
Siemens PLM Software
Something wrong?
std::string a(“Test”)
std::string b = std::move(a);
…
a += “test”;
Unrestricted © Siemens AG 2014 All rights reserved.
Page 58
2014-12-17
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
Unrestricted © Siemens AG 2014 All rights reserved.
Page 59
2014-12-17
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)
• …
Unrestricted © Siemens AG 2014 All rights reserved.
Page 60
2014-12-17
Siemens PLM Software
Move semantics && rvalue references
• Part 2
1. Alternative implementations for move operations
2. Moving into C++14 lambda expressions
3. 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 references
5. Perfect forwarding
6. std::forward
7. How to pass arguments
8. Emplace
Unrestricted © Siemens AG 2014 All rights reserved.
Page 61
2014-12-17
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
Unrestricted © Siemens AG 2014 All rights reserved.
Page 62
2014-12-17
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&&
Unrestricted © Siemens AG 2014 All rights reserved.
Page 63
2014-12-17
Siemens PLM Software
Questions?
Restricted © Siemens AG 2014 All rights reserved.
Smarter decisions, better products.

similar documents