4. 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
5. 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
6. 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 (an “eXpiring” value) - is an expression that
identifies 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
7. std::cout << &bar().member; // ?
Lvalue And Rvalue, Example 2
Foo& foo(); // `foo()` is lvalue
foo() = 42; // OK
std::cout << &foo(); // OK
Bar bar(); // `bar()` is prvalue
Bar && pub(); // `pub()` is xvalue
Bar b2 = bar(); // OK
bar() = b2; // cannot assign to an rvalue
std::cout << &bar(); // cannot take the address of an rvalue
std::cout << &pub(); // …
!7
std::cout << &bar().member; // reference to a member of an rvalue
// is an rvalue!
8. Lvalue References
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. (Very old Visual C++ versions
bind 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
9. 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!
10. 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
12. 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 ...
rv
allocate
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
13. Moved-From Objects
❖ Moving-from does not abolish destructing. The object’s destructor
is be 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. there is guaranty
that moved-from STD smart pointers are empty.
!13
F1 23 4C DB 98 73 11 ...
rv
lv
X
14. 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
16. Adding Move Semantics To a Class
❖ There are two new special member functions:
❖ Move constructor:
Foo::Foo(Foo&& op);
❖ Move assignment operator:
Foo& operator= (Foo&& op);
❖ 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
17. 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
19. 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 traits
!19
20. 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
21. 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 talk
!21
22. Optimization
❖ Copy elision does elision of both copy and move
operations
❖ When copy elision is not applicable, compiler prefers
moving over copying.
!22
23. 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
25. Named Rvalue Reference Is Not an Rvalue
Foo::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 even safer
: m_bar(std::move(rh).m_bar) {} //[5] OK, ref to rvalue member is
// rvalue
!25
26. Returning of Rvalue Reference
Foo && 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
27. ❖ 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
28. 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
29. 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
31. Move Semantics And Exception Safety
For 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
32. Useful Links
Alex Allain, Move semantics and rvalue references in C++11
http://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/
Value categories
https://en.cppreference.com/w/cpp/language/value_category
C++11 Standard (final plus minor editorial changes)
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf
!32