+ All Categories
Home > Software > Hot C++: Rvalue References And Move Semantics

Hot C++: Rvalue References And Move Semantics

Date post: 21-Jul-2015
Category:
Upload: andrey-upadyshev
View: 310 times
Download: 2 times
Share this document with a friend
Popular Tags:
33
Andrey Upadyshev Hot C++: Rvalue References And Move Semantics Licensed under a CC BY-SA 4.0 License. Version of 2015.06.19 1
Transcript

Andrey Upadyshev

Hot C++:Rvalue References And Move Semantics

Licensed under a CC BY-SA 4.0 License. Version of 2015.06.191

Overview

❖ Rvalue references!

❖ Move semantics!

❖ It’s well assisted!!

❖ More ways to shoot yourself in the foot

2

Rvalue References

3

Lvalue And Rvalue (how came from C)

• lvalue - may appear on the left hand side of an assignment, represents storage region locator value, i.e. evaluates to object identity.!

• All the rest is non-lvalue or rvalue (because can appear on the right hand side of assignment only)

4

Lvalue And Rvalue, Example 1

int a = 42; // OK lvalue on left side of assignment!int b = 43; // and rvalue on right side!!a = b; // OK, lvalue may be on any side of assignment!b = a; // OK, lvalue may be on any side of assignment!!int c = a * b; // OK, rvalue on right side of assignment!a * b = 42; // err, rvalue on left hand side of assignment !int *p = &i; // OK, i is an lvalue!int *p1 = &43; // err, cannot take the address of an rvalue!

5

Value Categories (C++11)• lvalue - is an expression that identifies a non-

temporary object!

• prvalue (pure rvalue) - is an expression that identifies a temporary object or is a value not associated with any object (C++03 rvalue)!• Ex: The result of calling a function whose return

type is a value!

• xvalue - is an expression that identifies an "eXpiring" object, that is, the object that may be moved from!• Ex: The result of calling a function whose return

type is an rvalue reference

6

Simplified!

}rvalues

std::cout << &bar().member; // ?

Lvalue And Rvalue, Example 2Foo& foo(); // `foo()` is lvaluefoo() = 42; // OKstd::cout << &foo(); // OK

Bar bar(); // `bar()` is prvalueBar && pub(); // `pub()` is xvalueBar b2 = bar(); // OKbar() = b2; // cannot assign to an rvaluestd::cout << &bar(); // cannot take the address of an rvaluestd::cout << &pub(); // …

7

std::cout << &bar().member; // reference to a member of an rvalue // is an rvalue!

Lvalue Referencesvoid foo(const Bar& bar); //[1] Binds to lvalue and rvalue. Can not modify bar.!

void foo(Bar& bar); //[2] Binds to lvalue only. Can modify bar. (Old Visual Studios binds it to rvalue too. But this does not conform to the Standard)

8

Bar read_bar(const char *filename); foo(read_bar(“bar.txt”)); ? foo(read_bar(“bar.txt”)); // [1] => bar is const

Rvalue References

9

void foo(const Bar& bar); //[1]

Binds to lvalue and rvalue. Can not modify bar.!void foo(Bar& bar); //[2]

Binds to lvalue only. Can modify bar.!void foo(Bar && bar); //[3]

Binds to rvalue only. Takes precedence over lvalue overloads. Can modify bar.!

void foo(const Bar && bar); //[4] Binds to rvalue only. Takes precedence over lvalue overloads. Can not modify bar. [Almost] has no useful meaning. Use const ref overload instead.

foo(read_bar(“bar.txt”)); // [3] => bar is mutable!

Why Do I Need Them At All? void foo(Bar && rv);!

❖ In short: to use move semantics!!

❖ Longer:!

❖ Because rvalue references bind to rvalue, they can be used to extend the lifetime of a modifiable temporary (an rvalue is a temporary, remember?).!

❖ You can do funny things with temporary, e.g. safely steal any data you need from it (nobody cares, heh). It’s called move semantics.

10

Move Semantics

11

Move Semantics In Example

Copying!MemBuf lv = rv;

class MemBuf { void *m_data; size_t m_size; ... };

12

F1 23 4C DB 98 73 11 ...rvallocate and copy

F1 23 4C DB 98 73 11 ...

lv contains a copy of rv's content

lv

Moving!MemBuf lv = std::move(rv);

F1 23 4C DB 98 73 11 ...rv

lv

lv grabs (steals) content of rv rv in moved-from state

X

Moved-From Objects

❖ Moving-from does not abolish destructing. The object’s destructor is called anyway!!

❖ Moved-from object should be placed in a valid but unspecified state, so it is safe to:!❖ destroy it!❖ assign one a new value (is assignable)!

❖ A type may provide more strong guaranty. E.g. STL’s smart pointers guaranty that moved-from object is empty.

13

F1 23 4C DB 98 73 11 ...rvlvX

Why Move Semantics?The best possible performance for classes with expensive copy while

keeping the clear interface.!

❖ std::vector<HugeObject> performing bad in C++03:!❖ Copies elements on insertion and even more on growing.!❖ Can be passed around only wrapped with smart pointer.!

❖ So we used std::vector<std::shared_ptr<HugeObject>> or boost::ptr_vector<HugeObject> to gain performance but got new problems:!❖ Extra allocation and extra level of indirection (performance!)!❖ Extra complexity!❖ Doesn’t work smoothly with STL algorithms (OK, ptr_vector does)!

❖ Thanks to move semantics std::vector<HugeObject> become good in C++11 (finally!):!❖ Elements are moved on insertion and on growing!❖ No [smart] pointer involved overhead!❖ Clear and simple, STL algorithms works perfect!❖ Can be moved around

14

Why Move Semantics?

❖ Clearer interface for non-copyable but movable objects (like files, threads etc):!

❖ std::auto_ptr<File> openFile( const char *filename); // C++03!

❖ File openFile(const char *filename); // C++11

15

Adding Move Semantics To a Class❖ There are two new special member functions:!

❖ Move constructor: Foo::Foo(Foo && rv);!

❖ Move assignment operator: Foo& operator= (Foo && rv);!

❖ Like copy ctor/assignment operator, but moves from its argument.

❖ The functions are compiler generated by default (use it!) but may be user provided, explicitly defaulted or deleted.!

❖ Compiler uses similar rules as for copy ctor/copy assignment operator generation.!

❖ Visual Studio before 2015 does not generate move ctor/move assignment. You must do it manually.

16

Forcing Move Semantics

template<class T>void swap_opt(T& a, T& b){ T tmp(a); a = b; b = tmp;}!

Hm, copying…

std::move just casts lvalue to rvalue (no moving itself!) what allows a move semantics to be used. Scott Meyers said that perhaps one should be called rvalue_cast

17

template<class T>void swap_opt(T& a, T& b){ T tmp(std::move(a)); a = std::move(b); b = std::move(tmp);}!

Moving!!!

Let’s implement optimized swap that utilize move semantics.!For simplicity, assume that it will be applied only to movable types.

Forcing move semantics leaves moved from objects behind, so must be uses carefully:! std::string tmp("bla-bla"); std::string s(std::move(tmp)); std::cout << tmp; // Using of 'moved from' object (e.g. by mistake) for // other then assigning. Undefined behavior for most types

It’s Well Assisted

18

Supported By STL❖ C++11 STL is significantly extended to support rvalue references

and move semantics:!

❖ All containers and other types storing user types (pair, tuple, future, function, smart pointers etc) are move-aware.!

❖ New algorithms: move, move_forward!

❖ New iterator adapter: move_iterator!

❖ Movable, non-copyable unique_ptr replaces flawy auto_ptr (which is deprecated)!

❖ Optimized swap for movable types!

❖ New type traits19

Ref Qualifiers❖ Member functions can be specified with ref-qualifier & or &&, placed

after cv-qualifier:!

❖ const Foo& Bar::foo() const & { // [1] return m_foo;}

❖ Foo&& Bar::foo() && { // [2] return std::move(m_foo); // `*this` is always} // lvalue

❖ Allow to have different overloads for lvalue and rvalue (temporary).!

❖ No ref-qualifier means “whatever” (for backward compatibility)!

❖ A function with a ref-qualifier can not overload with a function without one.

20

Perfect Forwarding & Forwarding References

❖ Perfect forwarding is finally possible! !

❖ Thanks to forwarding (aka universal) references and new reference collapsing rules:!❖ template<class P1, class P2>void create_foo(P1 && p1, P2 && p2) { Foo foo(std::forward<P1>(p1), std::forward<P2>(p2)); …}

❖ Looks even better with variadic templates (not a sarcasm!)!

❖ It is a big topic for another meeting21

Optimization

❖ Copy elision does elision of both copy and move operations!

❖ When copy elision is not applicable, compiler prefers moving over copying.

22

Special Member Functions❖ Move constructor and move assignment operator aren’t generated if copy

constructor, copy assignment operator or destructor is explicitly declared.!❖ Copy constructor and copy assignment operator aren't generated if move

constructor or move assignment operator is explicitly declared!❖ Not supported before Visual Studio 2015!

❖ Rule of three become rule of five:If a class defines one of the following it should probably explicitly define all five:!

•destructor!•copy constructor!•copy assignment operator!•move constructor!•move assignment operator

23

More Ways To Shoot Yourself In The Foot

24

Named Rvalue Reference Is Not an RvalueFoo::Foo(Bar && bar) : m_bar(bar) {} //[1] Is something wrong?

Named rvalue reference identifies an object => it is lvalue. i.e. bar is copied unless move semantics is forced!

Foo::Foo(Bar && bar) : m_bar(std::move(bar)) {} //[2] OK

Foo::Foo(Foo && rh) : m_bar(rh.m_bar) {} //[3] copy, ref to lvalue member is an lvalue

Foo::Foo(Foo && rh) : m_bar(std::move(rh.m_bar)) {} //[4] OK // or : m_bar(std::move(rh).m_bar) {} //[5] OK, ref to rvalue member is // rvalue

25

Returning of Rvalue ReferenceFoo && make_foo() { //[1] Is something wrong? return Foo(42);}

An rvalue reference is a reference. Returning a reference to a local object is bad. Return by value instead!Foo make_foo() { //[2] OK return Foo(42);}

Return rvalue reference only when you really know that you need this. A right example is std::move:!

template<class T>typename std::remove_reference<T>::type&& std::move(T && t) { return static_cast<typename std::remove_reference<T>::type &&>(t); }

26

❖ Applying std::move to a const object does not activate move semantics! You quietly got copy instead of move.!

❖ const Foo f = make_foo();return Bar(std::move(f)); // Moving of `f` is blocked

❖ std::move does not remove object constness!

❖ No compilation error generated in such case.!

❖ Confusing unjustified behavior. One can write something like safe_move to protect himself.

std::move And Const Objects

27

Move Semantics and RVO

28

struct Foo {…};

Foo make_foo1() { Foo r(…); return std::move(r);}

Everything correct?!RVO is blocked, r is moved or copied!

Foo make_foo1() { Foo r(…); return r;}

RVO is applied, no move or copy

Move Semantics and RVO

29

struct Bar {…}; struct Foo { Foo(const Bar& rh); Foo(Bar && rh); …};

Foo make_foo2() { Bar bar(…); return bar; } Everything correct?!Actually it is `return Foo(bar)`. bar is copied!

Foo make_foo2() { Bar bar(…); return std::move(bar);} Actually it is `return Foo(std::move(bar))`. bar is moved. We can’t get any better here

Move Semantics And Exception Safety❖ // Huge object

struct Elephant { Elephant(const Elephant& op); Elephant(Elephant && op); Elephant& operator= (const Elephant& op); Elephant& operator= (Elephant && op); …}; std::vector<Elephant> all_elephants;… // Heavily use it

❖ Does everything look good?!

30

Move Semantics And Exception SafetyFor good reason STL containers may copy elements internally unless ones' move constructor doesn't throw. Special traits are used:!

std::move_if_noexcept!std::is_nothrow_move_constructible!

To get rid of copy, add noexcept to the move special functions (only if function really doesn't throw, no cheating, please!):!Elephant(Elephant && op) noexcept;

Note that noexcept is not supported in Visual Studio before 2015, use macro like BOOST_NOEXCEPT (<boost/config/suffix.hpp>):!Elephant(Elephant && op) BOOST_NOEXCEPT;

31

Useful LinksAlex Allain, Move semantics and rvalue references in C++11http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html!

Dave Abrahams, Exceptionally Moving! http://web.archive.org/web/20130524084627/http://cpp-next.com/archive/2009/10/exceptionally-moving/!

Stephan T. Lavavej, Don’t Help the Compiler http://channel9.msdn.com/Events/GoingNative/2013/Don-t-Help-the-Compiler!

Andrzej Krzemieński, Ref-qualifiers https://akrzemi1.wordpress.com/2014/06/02/ref-qualifiers/!

Max Galkin. C++ curiosities: one does not simply move a const object http://yacoder.net/blog/2015/05/06/cpp-curiosities-one-does-not-simply-move-a-const-object/!

C++ Reference http://en.cppreference.com/w/!

C++11 Standard (final plus minor editorial changes)!http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf

32

Questions?

33

I HAVE NO IDEAWHAT’S GOING ON.


Recommended