Date post: | 25-May-2015 |
Category: |
Technology |
Upload: | andrey-upadyshev |
View: | 1,922 times |
Download: | 1 times |
Andrey Upadyshev
Hot C++11:Rvalue References And Move Semantics
Licensed under a CC BY-SA 4.0 License. Version of 2015.03.26
Lvalue And Rvalue (how comes from C)
• lvalue - may appear on the left or on the right hand side of an assignment!
• rvalue - can only appear on the right hand side of an assignment
2
Lvalue And Rvalue, Example 1int 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; // error, rvalue on left hand side of assignment
3
Lvalue And Rvalue (C++ style)• lvalue - locator value: refers to a defined region of
storage (possible to get address of)!
• rvalue - is an expression that is not an lvalue!
• rvalue - is expression resulting to temporary object
4
Lvalue And Rvalue, Example 2int i = 42; int *p = &i; // ok, i is an lvalue int *p1 = &43; // error, cannot take the address of an rvalue !int& foo() { ... } foo() = 42; // ok, foo() is an lvalue int *p2 = &foo(); // ok, foo() is an lvalue !int bar() { ... } int i = bar(); // ok, foobar() is an rvalue bar() = 42; // error, cannot assign to an rvalue int *p3 = &bar(); // error, cannot take the address of an rvalue
5
Rvalue Referencevoid foo(const Bar& bar); ! Can not modify bar. Accepts lvalue and rvalue.!
void foo(Bar& bar); ! Can modify bar. Accepts lvalue only (Visual Studio also accepts rvalue. But this does not conform the Standard)!
void foo(Bar&& bar); ! Can modify bar. Accepts rvalue only. Takes precedence over Bar& and const Bar& overloads.!
void foo(const Bar&& bar); ! Allowed but has no useful meaning. Use const Bar& instead.
6
Move Semantics In Example
Copying!MemBuf a = ...;MemBuf b(a);
Copy ctor: MemBuf(const MemBuf& op);
7
class MemBuf { void *m_data; size_t m_size; ... };
Similar reasoning applies to the copy/move assignment.
F1 23 4C DB 98 73 11 ...a.m_data = addr1 a.m_size = n
b.m_data = addr2 b.m_size = n// a is not changed
allocate and copy
F1 23 4C DB 98 73 11 ...
Moving!MemBuf a = ...;MemBuf b(std::move(a));
Move ctor: MemBuf(MemBuf&& op);
F1 23 4C DB 98 73 11 ...a.m_data = addr1 a.m_size = n
b.m_data = addr1 b.m_size = na.m_data = nullptr a.m_size = 0
Note that "cleared out" object must stay in
consistent state}
Why Move Semantics?Useful for:!A. Better performance for classes with expensive
copy (like STL containers):!void push_back(const string& s); // copies s void push_back(string&& s); // moves s (no copy)
B. Clearer interface working with non-copyable objects (like files, threads etc):!
std::auto_ptr<File> openFile(filename); File openFile(filename);
8
Forcing Move SemanticsX a, b; std::swap(a, b); !template<class T> void std::swap(T& a, T& b) { T tmp(a); a = b; b = tmp; } !template<class T> void std::swap(T& a, T& b) { T tmp(std::move(a)); a = std::move(b); b = std::move(tmp); }
Three copying (C++98)
Three movings, no copying!(C++11, enabled only if T is movable)
std::move does not move any data but casts lvalue to rvalue => forces move semantics
9
Rvalue Reference Is Not Rvaluevoid foo1(Bar&& arg) { Bar b = arg; // arg copied. arg is a named var so it's lvalue not rvalue } !void foo2(Bar&& arg) { Bar b = std::move(arg); // arg moved (move semantics forced) } !class Foo3 { Bar m_b; Foo3(Foo3&& arg) : m_b(arg.m_b) // arg.m_b copied. arg is a named var so it's lvalue not rvalue! {} };!!class Foo4 { Bar m_b; Foo4(Foo4&& arg) : m_b(std::move(arg.m_b)) // arg.m_b moved (move semantics forced), or! : m_b(std::move(arg).m_b) // arg.m_b moved (move semantics forced & member of rvalue is rvalue) {} };
10
Move Semantics and RVOclass Foo {...}; // Assume Foo is movable!!Foo bar1() { Foo r; return std::move(r); // RVO blocked => r is moved. OK but could be better}!Foo bar2() { Foo r; return r; // RVO used => perfect}!class Bar {...}; // Assume Bar is movable and Foo has ctor Foo(Bar&&)!Foo bar3() { Bar tmp; return tmp; // No RVO for tmp (because tmp is not Foo). It actually returns Foo(tmp) => tmp is copied!}!Foo bar4() { Bar tmp; return std::move(tmp); // No RVO. It actually returns Foo(std::move(tmp)) => moving is the best we can get}
11
Move Semantics And Exception SafetyFor good reason STL containers may copy elements internally unless ones' move constructor doesn't throw, check:!
• std::move_if_noexcept • std::is_nothrow_move_constructible
To optimize, just add noexcept (only if function really doesn't throw, no cheating, please!):!
Foo::Foo(Foo&& arg) noexcept;!
Note that it is not supported in Visual Studio before 2015, use macro like BOOST_NOEXCEPT (<boost/config/suffix.hpp>):!
Foo::Foo(Foo&& arg) BOOST_NOEXCEPT;
12
Compiler-Generated Functions• Compiler can generate move constructor and move assignment operator!
• Not generated if explicitly declared either one of:!• destructor!• copy constructor!• copy assignment operator!• move constructor!• move assignment operator!
• Change from C++98: copy constructor and copy assignment operator aren't generated if move constructor or move assignment operator is explicitly declared!
• Not supported before Visual Studio 2014 CTP3
rule of five
13
}
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/!
Move constructors http://en.cppreference.com/w/cpp/language/move_constructor!
C++11 Standard (final plus minor editorial changes)!http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf
14