Unrestricted © Siemens AG 2014 All rights reserved.Smarter decisions, better products. Move...

45
Unrestricted © Siemens AG 2014 All rights reserved. Smarter decisions, better products. Move semantics, rvalue references && perfect forwarding, part 1 Bert Rodiers, Software Architect @ Siemens Industry Software NV

Transcript of Unrestricted © Siemens AG 2014 All rights reserved.Smarter decisions, better products. Move...

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&&

Restricted © Siemens AG 2014 All rights reserved. Smarter decisions, better products.

Questions?