The move library is a collection of utilities for creating and using types that leverage return value optimization (RVO) to avoid unnecessary copies. More...
Detailed DescriptionTutorialUser defined types often have remote parts either because they are implemented using a pointer-to-implementation or are variable sized. Such objects can be expensive to copy and are often copied unnecessarily when they are returned from functions or stored in other objects or containers. The Move Library is a collection of utilities to implement types which can be moved to elide copying in such situations as well as utilities to assist in moving value.
A movable type models Movable. There are three components of a movable type:
A typical implementation of the move-ctor will simply extract the remote part, leaving the source in a destructible state. The assignment operator takes the operand parameter by value. Typically the simplest way to destory the local remote part and consume the remote part of the operand is to swap contents with the operand. This is similar to the copy-ctor and swap idiom for implementing assignment. Listing 1 shows an example movable class that implements a typical pointer-to-implementation (PiPl) idiom and shows that it can be used as any regular type. #include <iostream> #include <algorithm> #include <boost/operators.hpp> #include <adobe/move.hpp> using std::swap; struct implementation : boost::equality_comparable<implementation> { explicit implementation(int x = 0) : member(x) { } implementation(const implementation& x) : member(x.member) { std::cout << "copy remote part: " << member << std::endl; } implementation& operator=(const implementation& x) { member = x.member; std::cout << "assign remote part: " << member << std::endl; return *this; } friend bool operator==(const implementation& x, const implementation& y) { return x.member == y.member; } int member; }; class movable : public boost::equality_comparable<movable> { public: // model concept Regular explicit movable(int x = 0) : member(new implementation(x)) { } ~movable() { delete member; } movable(const movable& x) : member(new implementation(*x.member)) { } // operator=() implemented below friend bool operator==(const movable& x, const movable &y) { return *x.member == *y.member; } friend void swap(movable& x, movable& y) { swap(x.member, y.member); } // model concept Movable // move-ctor assumes ownership of remote part movable(adobe::move_from<movable> x) : member(x.source.member) { x.source.member = 0; } // operator=() on a movable type takes parameter by value and consumes it movable& operator=(movable x) { swap(*this, x); return *this; } private: implementation* member; }; int main() { movable x(10); movable y = x; return 0; } copy remote part: 10
We can return a movable type from a function by value and unnessary copies will be avoided as Listing 2 illustrates: //... movable f(int x, int y) { return movable(x * y); } int main() { movable x = f(10, 5); movable y; y = f(4, 3); return 0; } In this example it is not necessary to make any copies. The result of f() is constructed directly in place for x through a compiler optimization known as return value optimization or RVO. In the case of assigning to y, the same optimization allows the compiler to construct the operand for assignment as the result of f() which is them moved into y.
A sink is any function that copies it's argument, usually for the purpose of storing it. A sink is often a constructor or an insert function on a container. The //... struct sink { explicit sink(movable x) : member(adobe::move(x)) { } movable member; }; int main() { movable x = f(10, 5); sink y(x); // must copy. sink z(f(20, 2)); // no copy. return 0; } copy remote part: 50 Here again unnessary copies are eliminated. Although adobe::move() can be used anytime to force the move of an object, it should only be used as part of an explicit sink function otherwise it hinders the understanding of code.
There are many utilities as part of the move library which can be used to move elements instead of copying them. These are useful when building containers or dealing with sink operations which must manage a collection of movable objects. Generally these operations parallel the associated copying algorithms from STL. Examples:
The If a sink function is a member of a template class, the same issue with regard to unnecessary copies can occur. In this case, it is desirable to distinguish between the a copy and move sink as above but also to allow implicit conversions to the type stored in the container. To allow this use the two argument form of
to be written
Function Documentation
ConvertibleToRange version of move. Similar to copy but with move semantics, for movable types, otherwise with copy semantics.
This version of move is selected when T is_movable . It in turn calls the move constructor. This call, with the help of the return value optimization, will cause x to be moved instead of copied to its destination. See adobe/test/move/main.cpp for examples.
ConvertibleToRange version of move_backwards. Similar to std::copy_backwards but with move semantics, for movable types, otherwise with copy semantics. |