]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | [/ |
2 | / Copyright (c) 2008-2010 Ion Gaztanaga | |
3 | / | |
4 | / Distributed under the Boost Software License, Version 1.0. (See accompanying | |
5 | / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
6 | /] | |
7 | [library Boost.Move | |
8 | [quickbook 1.5] | |
9 | [authors [Gaztanaga, Ion]] | |
10 | [copyright 2008-2014 Ion Gaztanaga] | |
11 | [id move] | |
12 | [dirname move] | |
13 | [purpose Move semantics] | |
14 | [license | |
15 | Distributed under the Boost Software License, Version 1.0. | |
16 | (See accompanying file LICENSE_1_0.txt or copy at | |
17 | [@http://www.boost.org/LICENSE_1_0.txt]) | |
18 | ] | |
19 | ] | |
20 | ||
21 | [important To be able to use containers of movable-only values you will need to use containers | |
22 | supporting move semantics, like [*Boost.Container] containers] | |
23 | ||
24 | [note Tested compilers: MSVC-7.1, 8.0, 9.0, GCC 4.3-MinGW in C++03 and C++0x modes, Intel 10.1] | |
25 | ||
26 | [section:what_is_boost_move What is Boost.Move?] | |
27 | ||
28 | Rvalue references are a major C++0x feature, enabling move semantics for C++ values. However, we | |
29 | don't need C++0x compilers to take advantage of move semanatics. [*Boost.Move] emulates C++0x | |
30 | move semantics in C++03 compilers and allows writing portable code that works optimally in C++03 | |
31 | and C++0x compilers. | |
32 | ||
33 | [endsect] | |
34 | ||
35 | [section:introduction Introduction] | |
36 | ||
37 | [note | |
38 | ||
39 | The first 3 chapters are the adapted from the article | |
40 | [@http://www.artima.com/cppsource/rvalue.html ['A Brief Introduction to Rvalue References]] | |
41 | by Howard E. Hinnant, Bjarne Stroustrup, and Bronek Kozicki | |
42 | ||
43 | ] | |
44 | ||
45 | Copying can be expensive. For example, for vectors `v2=v1` typically involves a function call, | |
46 | a memory allocation, and a loop. This is of course acceptable where we actually need two copies of | |
47 | a vector, but in many cases, we don't: We often copy a `vector` from one place to another, just to | |
48 | proceed to overwrite the old copy. Consider: | |
49 | ||
50 | [c++] | |
51 | ||
52 | template <class T> void swap(T& a, T& b) | |
53 | { | |
54 | T tmp(a); // now we have two copies of a | |
55 | a = b; // now we have two copies of b | |
56 | b = tmp; // now we have two copies of tmp (aka a) | |
57 | } | |
58 | ||
59 | But, we didn't want to have any copies of a or b, we just wanted to swap them. Let's try again: | |
60 | ||
61 | [c++] | |
62 | ||
63 | template <class T> void swap(T& a, T& b) | |
64 | { | |
65 | T tmp(::boost::move(a)); | |
66 | a = ::boost::move(b); | |
67 | b = ::boost::move(tmp); | |
68 | } | |
69 | ||
70 | This `move()` gives its target the value of its argument, but is not obliged to preserve the value | |
71 | of its source. So, for a `vector`, `move()` could reasonably be expected to leave its argument as | |
72 | a zero-capacity vector to avoid having to copy all the elements. In other words, [*move is a potentially | |
73 | destructive copy]. | |
74 | ||
75 | In this particular case, we could have optimized swap by a specialization. However, we can't | |
76 | specialize every function that copies a large object just before it deletes or overwrites it. That | |
77 | would be unmanageable. | |
78 | ||
79 | In C++0x, move semantics are implemented with the introduction of rvalue references. They allow us to | |
80 | implement `move()` without verbosity or runtime overhead. [*Boost.Move] is a library that offers tools | |
81 | to implement those move semantics not only in compilers with `rvalue references` but also in compilers | |
82 | conforming to C++03. | |
83 | ||
84 | [endsect] | |
85 | ||
86 | [section:implementing_movable_classes Implementing copyable and movable classes] | |
87 | ||
88 | [import ../example/doc_clone_ptr.cpp] | |
89 | ||
90 | [section:copyable_and_movable_cpp0x Copyable and movable classes in C++0x] | |
91 | ||
92 | Consider a simple handle class that owns a resource and also provides copy semantics | |
93 | (copy constructor and assignment). For example a `clone_ptr` might own a pointer, and call | |
94 | `clone()` on it for copying purposes: | |
95 | ||
96 | [c++] | |
97 | ||
98 | template <class T> | |
99 | class clone_ptr | |
100 | { | |
101 | private: | |
102 | T* ptr; | |
103 | ||
104 | public: | |
105 | // construction | |
106 | explicit clone_ptr(T* p = 0) : ptr(p) {} | |
107 | ||
108 | // destruction | |
109 | ~clone_ptr() { delete ptr; } | |
110 | ||
111 | // copy semantics | |
112 | clone_ptr(const clone_ptr& p) | |
113 | : ptr(p.ptr ? p.ptr->clone() : 0) {} | |
114 | ||
115 | clone_ptr& operator=(const clone_ptr& p) | |
116 | { | |
117 | if (this != &p) | |
118 | { | |
119 | T *p = p.ptr ? p.ptr->clone() : 0; | |
120 | delete ptr; | |
121 | ptr = p; | |
122 | } | |
123 | return *this; | |
124 | } | |
125 | ||
126 | // move semantics | |
127 | clone_ptr(clone_ptr&& p) | |
128 | : ptr(p.ptr) { p.ptr = 0; } | |
129 | ||
130 | clone_ptr& operator=(clone_ptr&& p) | |
131 | { | |
132 | if(this != &p) | |
133 | { | |
134 | std::swap(ptr, p.ptr); | |
135 | delete p.ptr; | |
136 | p.ptr = 0; | |
137 | } | |
138 | return *this; | |
139 | } | |
140 | ||
141 | // Other operations... | |
142 | }; | |
143 | ||
144 | `clone_ptr` has expected copy constructor and assignment semantics, duplicating resources when copying. | |
145 | Note that copy constructing or assigning a `clone_ptr` is a relatively expensive operation: | |
146 | ||
147 | [copy_clone_ptr] | |
148 | ||
149 | `clone_ptr` is code that you might find in today's books on C++, except for the part marked as | |
150 | `move semantics`. That part is implemented in terms of C++0x `rvalue references`. You can find | |
151 | some good introduction and tutorials on rvalue references in these papers: | |
152 | ||
153 | * [@http://www.artima.com/cppsource/rvalue.html ['A Brief Introduction to Rvalue References]] | |
154 | * [@http://blogs.msdn.com/vcblog/archive/2009/02/03/rvalue-references-c-0x-features-in-vc10-part-2.aspx ['Rvalue References: C++0x Features in VC10, Part 2]] | |
155 | ||
156 | When the source of the copy is known to be a `rvalue` (e.g.: a temporary object), one can avoid the | |
157 | potentially expensive `clone()` operation by pilfering source's pointer (no one will notice!). The move | |
158 | constructor above does exactly that, leaving the rvalue in a default constructed state. The move assignment | |
159 | operator simply does the same freeing old resources. | |
160 | ||
161 | Now when code tries to copy a rvalue `clone_ptr`, or if that code explicitly gives permission to | |
162 | consider the source of the copy a rvalue (using `boost::move`), the operation will execute much faster. | |
163 | ||
164 | [move_clone_ptr] | |
165 | ||
166 | [endsect] | |
167 | ||
168 | [section:copyable_and_movable_cpp03 Copyable and movable classes in portable syntax for both C++03 and C++0x compilers] | |
169 | ||
170 | Many aspects of move semantics can be emulated for compilers not supporting `rvalue references` | |
171 | and [*Boost.Move] offers tools for that purpose. With [*Boost.Move] we can write `clone_ptr` | |
172 | so that it will work both in compilers with rvalue references and those who conform to C++03. | |
173 | You just need to follow these simple steps: | |
174 | ||
175 | * Put the following macro in the [*private] section: | |
176 | [macroref BOOST_COPYABLE_AND_MOVABLE BOOST_COPYABLE_AND_MOVABLE(classname)] | |
177 | * Leave copy constructor as is. | |
178 | * Write a copy assignment taking the parameter as | |
179 | [macroref BOOST_COPY_ASSIGN_REF BOOST_COPY_ASSIGN_REF(classname)] | |
180 | * Write a move constructor and a move assignment taking the parameter as | |
181 | [macroref BOOST_RV_REF BOOST_RV_REF(classname)] | |
182 | ||
183 | Let's see how are applied to `clone_ptr`: | |
184 | ||
185 | [clone_ptr_def] | |
186 | ||
187 | [endsect] | |
188 | ||
189 | [*Question]: What about types that don't own resources? (E.g. `std::complex`?) | |
190 | ||
191 | No work needs to be done in that case. The copy constructor is already optimal. | |
192 | ||
193 | [endsect] | |
194 | ||
195 | [section:composition_inheritance Composition or inheritance] | |
196 | ||
197 | For classes made up of other classes (via either composition or inheritance), the move constructor | |
198 | and move assignment can be easily coded using the `boost::move` function: | |
199 | ||
200 | [clone_ptr_base_derived] | |
201 | ||
202 | [important Due to limitations in the emulation code, a cast to `Base &` is needed before moving the base part in the move | |
203 | constructor and call Base's move constructor instead of the copy constructor.] | |
204 | ||
205 | Each subobject will now be treated individually, calling move to bind to the subobject's move | |
206 | constructors and move assignment operators. `Member` has move operations coded (just like | |
207 | our earlier `clone_ptr` example) which will completely avoid the tremendously more expensive | |
208 | copy operations: | |
209 | ||
210 | [clone_ptr_move_derived] | |
211 | ||
212 | Note above that the argument x is treated as a lvalue reference. That's why it is necessary to | |
213 | say `move(x)` instead of just x when passing down to the base class. This is a key safety feature of move | |
214 | semantics designed to prevent accidently moving twice from some named variable. All moves from | |
215 | lvalues occur explicitly. | |
216 | ||
217 | [endsect] | |
218 | ||
219 | [section:movable_only_classes Movable but Non-Copyable Types] | |
220 | ||
221 | Some types are not amenable to copy semantics but can still be made movable. For example: | |
222 | ||
223 | * `unique_ptr` (non-shared, non-copyable ownership) | |
224 | * A type representing a thread of execution | |
225 | * A type representing a file descriptor | |
226 | ||
227 | By making such types movable (though still non-copyable) their utility is tremendously | |
228 | increased. Movable but non-copyable types can be returned by value from factory functions: | |
229 | ||
230 | [c++] | |
231 | ||
232 | file_descriptor create_file(/* ... */); | |
233 | //... | |
234 | file_descriptor data_file; | |
235 | //... | |
236 | data_file = create_file(/* ... */); // No copies! | |
237 | ||
238 | In the above example, the underlying file handle is passed from object to object, as long | |
239 | as the source `file_descriptor` is a rvalue. At all times, there is still only one underlying file | |
240 | handle, and only one `file_descriptor` owns it at a time. | |
241 | ||
242 | To write a movable but not copyable type in portable syntax, you need to follow these simple steps: | |
243 | ||
244 | * Put the following macro in the [*private] section: | |
245 | [macroref BOOST_MOVABLE_BUT_NOT_COPYABLE BOOST_MOVABLE_BUT_NOT_COPYABLE(classname)] | |
246 | * Write a move constructor and a move assignment taking the parameter as | |
247 | [macroref BOOST_RV_REF BOOST_RV_REF(classname)] | |
248 | ||
249 | Here's the definition of `file descriptor` using portable syntax: | |
250 | ||
251 | [import ../example/doc_file_descriptor.cpp] | |
252 | [file_descriptor_def] | |
253 | ||
254 | [/ | |
255 | /Many standard algorithms benefit from moving elements of the sequence as opposed to | |
256 | /copying them. This not only provides better performance (like the improved `swap` | |
257 | /implementation described above), but also allows these algorithms to operate on movable | |
258 | /but non-copyable types. For example the following code sorts a `vector<unique_ptr<T>>` | |
259 | /based on comparing the pointed-to types: | |
260 | / | |
261 | /[c++] | |
262 | / | |
263 | / struct indirect_less | |
264 | / { | |
265 | / template <class T> | |
266 | / bool operator()(const T& x, const T& y) | |
267 | / {return *x < *y;} | |
268 | / }; | |
269 | / ... | |
270 | / std::vector<std::unique_ptr<A>> v; | |
271 | / ... | |
272 | / std::sort(v.begin(), v.end(), indirect_less()); | |
273 | / | |
274 | / | |
275 | /As sort moves the unique_ptr's around, it will use swap (which no longer requires Copyability) | |
276 | /or move construction / move assignment. Thus during the entire algorithm, the invariant that | |
277 | /each item is owned and referenced by one and only one smart pointer is maintained. If the | |
278 | /algorithm were to attempt a copy (say by programming mistake) a compile time error would result. | |
279 | /] | |
280 | ||
281 | [endsect] | |
282 | ||
283 | [section:move_and_containers Containers and move semantics] | |
284 | ||
285 | Movable but non-copyable types can be safely inserted into containers and | |
286 | movable and copyable types are more efficiently handled if those containers | |
287 | internally use move semantics instead of copy semantics. | |
288 | If the container needs to "change the location" of an element | |
289 | internally (e.g. vector reallocation) it will move the element instead of copying it. | |
290 | [*Boost.Container] containers are move-aware so you can write the following: | |
291 | ||
292 | [file_descriptor_example] | |
293 | ||
294 | [endsect] | |
295 | ||
296 | [section:construct_forwarding Constructor Forwarding] | |
297 | ||
298 | Consider writing a generic factory function that returns an object for a newly | |
299 | constructed generic type. Factory functions such as this are valuable for encapsulating | |
300 | and localizing the allocation of resources. Obviously, the factory function must accept | |
301 | exactly the same sets of arguments as the constructors of the type of objects constructed: | |
302 | ||
303 | [c++] | |
304 | ||
305 | template<class T> T* factory_new() | |
306 | { return new T(); } | |
307 | ||
308 | template<class T> T* factory_new(a1) | |
309 | { return new T(a1); } | |
310 | ||
311 | template<class T> T* factory_new(a1, a2) | |
312 | { return new T(a1, a2); } | |
313 | ||
314 | Unfortunately, in C++03 the much bigger issue with this approach is that the N-argument case | |
315 | would require 2^N overloads, immediately discounting this as a general solution. Fortunately, | |
316 | most constructors take arguments by value, by const-reference or by rvalue reference. If these | |
317 | limitations are accepted, the forwarding emulation of a N-argument case requires just N overloads. | |
318 | This library makes this emulation easy with the help of `BOOST_FWD_REF` and | |
319 | `boost::forward`: | |
320 | ||
321 | [import ../example/doc_construct_forward.cpp] | |
322 | [construct_forward_example] | |
323 | ||
324 | Constructor forwarding comes in handy to implement placement insertion in containers with | |
325 | just N overloads if the implementor accepts the limitations of this type of forwarding for | |
326 | C++03 compilers. In compilers with rvalue references perfect forwarding is achieved. | |
327 | ||
328 | [endsect] | |
329 | ||
330 | [section:move_return Implicit Move when returning a local object] | |
331 | ||
332 | The C++ standard specifies situations where an implicit move operation is safe and the | |
333 | compiler can use it in cases were the (Named) Return Value Optimization) can't be used. | |
334 | The typical use case is when a function returns a named (non-temporary) object by value | |
335 | and the following code will perfectly compile in C++11: | |
336 | ||
337 | [c++] | |
338 | ||
339 | //Even if movable can't be copied | |
340 | //the compiler will call the move-constructor | |
341 | //to generate the return value | |
342 | // | |
343 | //The compiler can also optimize out the move | |
344 | //and directly construct the object 'm' | |
345 | movable factory() | |
346 | { | |
347 | movable tmp; | |
348 | m = ... | |
349 | //(1) moved instead of copied | |
350 | return tmp; | |
351 | }; | |
352 | ||
353 | //Initialize object | |
354 | movable m(factory()); | |
355 | ||
356 | ||
357 | In compilers without rvalue references and some non-conforming compilers (such as Visual C++ 2010/2012) | |
358 | the line marked with `(1)` would trigger a compilation error because `movable` can't be copied. Using a explicit | |
359 | `::boost::move(tmp)` would workaround this limitation but it would code suboptimal in C++11 compilers | |
360 | (as the compile could not use (N)RVO to optimize-away the copy/move). | |
361 | ||
362 | [*Boost.Move] offers an additional macro called [macroref BOOST_MOVE_RET BOOST_MOVE_RET] that can be used to | |
363 | alleviate this problem obtaining portable move-on-return semantics. Let's use the previously presented | |
364 | movable-only `movable` class with classes `copyable` (copy-only type), `copy_movable` (can be copied and moved) and | |
365 | `non_copy_movable` (non-copyable and non-movable): | |
366 | ||
367 | [import ../example/copymovable.hpp] | |
368 | [copy_movable_definition] | |
369 | ||
370 | and build a generic factory function that returns a newly constructed value or a reference to an already | |
371 | constructed object. | |
372 | ||
373 | [import ../example/doc_move_return.cpp] | |
374 | [move_return_example] | |
375 | ||
376 | [*Caution]: When using this macro in a non-conforming or C++03 | |
377 | compilers, a move will be performed even if the C++11 standard does not allow it | |
378 | (e.g. returning a static variable). The user is responsible for using this macro | |
379 | only used to return local objects that met C++11 criteria. E.g.: | |
380 | ||
381 | [c++] | |
382 | ||
383 | struct foo | |
384 | { | |
385 | copy_movable operator()() const | |
386 | { | |
387 | //ERROR! The Standard does not allow implicit move returns when the object to be returned | |
388 | //does not met the criteria for elision of a copy operation (such as returning a static member data) | |
389 | //In C++03 compilers this will MOVE resources from cm | |
390 | //In C++11 compilers this will COPY resources from cm | |
391 | //so DON'T use use BOOST_MOVE_RET without care. | |
392 | return BOOST_MOVE_RET(copy_movable, cm); | |
393 | } | |
394 | ||
395 | static copy_movable cm; | |
396 | }; | |
397 | ||
398 | ||
399 | [*Note]: When returning a temporary object `BOOST_MOVE_REF` is not needed as copy ellision rules will work on | |
400 | both C++03 and C++11 compilers. | |
401 | ||
402 | [c++] | |
403 | ||
404 | //Note: no BOOST_MOVE_RET | |
405 | movable get_movable() | |
406 | { return movable(); } | |
407 | ||
408 | copy_movable get_copy_movable() | |
409 | { return copy_movable(); } | |
410 | ||
411 | copyable get_copyable() | |
412 | { return copyable(); } | |
413 | ||
414 | ||
415 | [endsect] | |
416 | ||
417 | [section:move_iterator Move iterators] | |
418 | ||
419 | [c++] | |
420 | ||
421 | template<class Iterator> | |
422 | class move_iterator; | |
423 | ||
424 | template<class It> | |
425 | move_iterator<It> make_move_iterator(const It &it); | |
426 | ||
427 | [classref boost::move_iterator move_iterator] is an iterator adaptor with the | |
428 | same behavior as the underlying iterator | |
429 | except that its dereference operator implicitly converts the value returned by the | |
430 | underlying iterator's dereference operator to a rvalue reference: `boost::move(*underlying_iterator)` | |
431 | It is a read-once iterator, but can have up to random access traversal characteristics. | |
432 | ||
433 | `move_iterator` is very useful because some generic algorithms and container insertion functions | |
434 | can be called with move iterators to replace copying with moving. For example: | |
435 | ||
436 | [import ../example/movable.hpp] | |
437 | [movable_definition] | |
438 | ||
439 | `movable` objects can be moved from one container to another using move iterators and insertion | |
440 | and assignment operations.w | |
441 | ||
442 | [import ../example/doc_move_iterator.cpp] | |
443 | [move_iterator_example] | |
444 | ||
445 | [endsect] | |
446 | ||
447 | [section:move_inserters Move inserters] | |
448 | ||
449 | Similar to standard insert iterators, it's possible to deal with move insertion in the same way | |
450 | as writing into an array. A special kind of iterator adaptors, called move insert iterators, are | |
451 | provided with this library. With regular iterator classes, | |
452 | ||
453 | [c++] | |
454 | ||
455 | while (first != last) *result++ = *first++; | |
456 | ||
457 | causes a range [first,last) to be copied into a range starting with result. The same code with | |
458 | result being a move insert iterator will move insert corresponding elements into the container. | |
459 | This device allows all of the copying algorithms in the library to work in the move insert mode | |
460 | instead of the regular overwrite mode. This library offers 3 move insert iterators and their | |
461 | helper functions: | |
462 | ||
463 | [c++] | |
464 | ||
465 | // Note: C models Container | |
466 | template <typename C> | |
467 | class back_move_insert_iterator; | |
468 | ||
469 | template <typename C> | |
470 | back_move_insert_iterator<C> back_move_inserter(C& x); | |
471 | ||
472 | template <typename C> | |
473 | class front_move_insert_iterator; | |
474 | ||
475 | template <typename C> | |
476 | front_move_insert_iterator<C> front_move_inserter(C& x); | |
477 | ||
478 | template <typename C> | |
479 | class move_insert_iterator; | |
480 | ||
481 | template <typename C> | |
482 | move_insert_iterator<C> move_inserter(C& x, typename C::iterator it); | |
483 | ||
484 | ||
485 | A move insert iterator is constructed from a container and possibly one of its iterators pointing | |
486 | to where insertion takes place if it is neither at the beginning nor at the end of the container. | |
487 | Insert iterators satisfy the requirements of output iterators. `operator*` returns the move insert | |
488 | iterator itself. The assignment `operator=(T& x)` is defined on insert iterators to allow writing | |
489 | into them, it inserts x right before where the insert iterator is pointing. In other words, an | |
490 | `insert iterator` is like a cursor pointing into the container where the insertion takes place. | |
491 | `back_move_iterator` move inserts elements at the end of a container, `front_insert_iterator` | |
492 | move inserts elements at the beginning of a container, and `move_insert_iterator` move inserts | |
493 | elements where the iterator points to in a container. `back_move_inserter`, `front_move_inserter`, | |
494 | and `move_inserter` are three functions making the insert iterators out of a container. Here's | |
495 | an example of how to use them: | |
496 | ||
497 | [import ../example/doc_move_inserter.cpp] | |
498 | [move_inserter_example] | |
499 | ||
500 | [endsect] | |
501 | ||
502 | [section:move_algorithms Move algorithms] | |
503 | ||
504 | The standard library offers several copy-based algorithms. Some of them, like `std::copy` or | |
505 | `std::uninitialized_copy` are basic building blocks for containers and other data structures. | |
506 | This library offers move-based functions for those purposes: | |
507 | ||
508 | [c++] | |
509 | ||
510 | template<typename I, typename O> O move(I, I, O); | |
511 | template<typename I, typename O> O move_backward(I, I, O); | |
512 | template<typename I, typename F> F uninitialized_move(I, I, F); | |
513 | template<typename I, typename F> F uninitialized_copy_or_move(I, I, F); | |
514 | ||
515 | ||
516 | The first 3 are move variations of their equivalent copy algorithms, but copy assignment and | |
517 | copy construction are replaced with move assignment and construction. The last one has the | |
518 | same behaviour as `std::uninitialized_copy` but since several standand library implementations | |
519 | don't play very well with `move_iterator`s, this version is a portable version for those | |
520 | willing to use move iterators. | |
521 | ||
522 | [import ../example/doc_move_algorithms.cpp] | |
523 | [move_algorithms_example] | |
524 | ||
525 | [endsect] | |
526 | ||
527 | [section:emulation_limitations Emulation limitations] | |
528 | ||
529 | Like any emulation effort, the library has some limitations users should take in | |
530 | care to achieve portable and efficient code when using the library with C++03 conformant compilers: | |
531 | ||
532 | [section:emulation_limitations_base Initializing base classes] | |
533 | ||
534 | When initializing base classes in move constructors, users must | |
535 | cast the reference to a base class reference before moving it or just | |
536 | use `BOOST_MOVE_BASE`. Example: | |
537 | ||
538 | [c++] | |
539 | ||
540 | Derived(BOOST_RV_REF(Derived) x) // Move ctor | |
541 | : Base(boost::move(static_cast<Base&>(x))) | |
542 | //... | |
543 | ||
544 | or | |
545 | ||
546 | [c++] | |
547 | ||
548 | Derived(BOOST_RV_REF(Derived) x) // Move ctor | |
549 | : Base(BOOST_MOVE_BASE(Base, x)) | |
550 | //... | |
551 | ||
552 | If casting is not performed the emulation will not move construct | |
553 | the base class, because no conversion is available from `BOOST_RV_REF(Derived)` to | |
554 | `BOOST_RV_REF(Base)`. Without the cast or `BOOST_MOVE_BASE` we might obtain a compilation | |
555 | error (for non-copyable types) or a less-efficient move constructor (for copyable types): | |
556 | ||
557 | [c++] | |
558 | ||
559 | //If Derived is copyable, then Base is copy-constructed. | |
560 | //If not, a compilation error is issued | |
561 | Derived(BOOST_RV_REF(Derived) x) // Move ctor | |
562 | : Base(boost::move(x)) | |
563 | //... | |
564 | ||
565 | [endsect] | |
566 | ||
567 | [section:template_parameters Template parameters for perfect forwarding] | |
568 | ||
569 | The emulation can't deal with C++0x reference collapsing rules that allow perfect forwarding: | |
570 | ||
571 | [c++] | |
572 | ||
573 | //C++0x | |
574 | template<class T> | |
575 | void forward_function(T &&t) | |
576 | { inner_function(std::forward<T>(t); } | |
577 | ||
578 | //Wrong C++03 emulation | |
579 | template<class T> | |
580 | void forward_function(BOOST_RV_REF<T> t) | |
581 | { inner_function(boost::forward<T>(t); } | |
582 | ||
583 | In C++03 emulation BOOST_RV_REF doesn't catch any const rlvalues. For more details on | |
584 | forwarding see [link move.construct_forwarding Constructor Forwarding] chapter. | |
585 | ||
586 | [endsect] | |
587 | ||
588 | [section:emulation_limitations_binding Binding of rvalue references to lvalues] | |
589 | ||
590 | The | |
591 | [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1690.html first rvalue reference] | |
592 | proposal allowed the binding of rvalue references to lvalues: | |
593 | ||
594 | [c++] | |
595 | ||
596 | func(Type &&t); | |
597 | //.... | |
598 | ||
599 | Type t; //Allowed | |
600 | func(t) | |
601 | ||
602 | ||
603 | Later, as explained in | |
604 | [@http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2812.html ['Fixing a Safety Problem with Rvalue References]] | |
605 | this behaviour was considered dangerous and eliminated this binding so that rvalue references adhere to the | |
606 | principle of type-safe overloading: ['Every function must be type-safe in isolation, without regard to how it has been overloaded] | |
607 | ||
608 | [*Boost.Move] can't emulate this type-safe overloading principle for C++03 compilers: | |
609 | ||
610 | [c++] | |
611 | ||
612 | //Allowed by move emulation | |
613 | movable m; | |
614 | BOOST_RV_REF(movable) r = m; | |
615 | ||
616 | [endsect] | |
617 | ||
618 | [section:assignment_operator Assignment operator in classes derived from or holding copyable and movable types] | |
619 | ||
620 | The macro [macroref BOOST_COPYABLE_AND_MOVABLE BOOST_COPYABLE_AND_MOVABLE] needs to | |
621 | define a copy constructor for `copyable_and_movable` taking a non-const parameter in C++03 compilers: | |
622 | ||
623 | [c++] | |
624 | ||
625 | //Generated by BOOST_COPYABLE_AND_MOVABLE | |
626 | copyable_and_movable &operator=(copyable_and_movable&){/**/} | |
627 | ||
628 | Since the non-const overload of the copy constructor is generated, compiler-generated | |
629 | assignment operators for classes containing `copyable_and_movable` | |
630 | will get the non-const copy constructor overload, which will surely surprise users: | |
631 | ||
632 | [c++] | |
633 | ||
634 | class holder | |
635 | { | |
636 | copyable_and_movable c; | |
637 | }; | |
638 | ||
639 | void func(const holder& h) | |
640 | { | |
641 | holder copy_h(h); //<--- ERROR: can't convert 'const holder&' to 'holder&' | |
642 | //Compiler-generated copy constructor is non-const: | |
643 | // holder& operator(holder &) | |
644 | //!!! | |
645 | } | |
646 | ||
647 | This limitation forces the user to define a const version of the copy assignment, | |
648 | in all classes holding copyable and movable classes which might be annoying in some cases. | |
649 | ||
650 | An alternative is to implement a single `operator =()` for copyable and movable classes | |
651 | [@http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/ using "pass by value" semantics]: | |
652 | ||
653 | [c++] | |
654 | ||
655 | T& operator=(T x) // x is a copy of the source; hard work already done | |
656 | { | |
657 | swap(*this, x); // trade our resources for x's | |
658 | return *this; // our (old) resources get destroyed with x | |
659 | } | |
660 | ||
661 | However, "pass by value" is not optimal for classes (like containers, strings, etc.) that reuse resources | |
662 | (like previously allocated memory) when x is assigned from a lvalue. | |
663 | ||
664 | [endsect] | |
665 | ||
666 | [section:templated_assignment_operator Templated assignment operator in copyable and movable types] | |
667 | ||
668 | ||
669 | [import ../example/doc_template_assign.cpp] | |
670 | ||
671 | Given a movable and copyable class, if a templated assignment operator (*) is added: | |
672 | ||
673 | [template_assign_example_foo_bar] | |
674 | ||
675 | C++98 and C++11 compilers will behave different when assigning from a `[const] Foo` lvalue: | |
676 | ||
677 | [template_assign_example_main] | |
678 | ||
679 | This different behaviour is a side-effect of the move emulation that can't be easily avoided by | |
680 | [*Boost.Move]. One workaround is to SFINAE-out the templated assignment operator with `disable_if`: | |
681 | ||
682 | [c++] | |
683 | ||
684 | template<class U> // Modified templated assignment | |
685 | typename boost::disable_if<boost::is_same<U, Foo>, Foo&>::type | |
686 | operator=(const U& rhs) | |
687 | { i = -rhs.i; return *this; } //(2) | |
688 | ||
689 | ||
690 | [endsect] | |
691 | ||
692 | [endsect] | |
693 | ||
694 | [section:how_the_library_works How the library works] | |
695 | ||
696 | [*Boost.Move] is based on macros that are expanded to true rvalue references in C++0x compilers | |
697 | and emulated rvalue reference classes and conversion operators in C++03 compilers. | |
698 | ||
699 | In C++03 compilers [*Boost.Move] defines a class named `::boost::rv`: | |
700 | ||
701 | [c++] | |
702 | ||
703 | template <class T> | |
704 | class rv : public T | |
705 | { | |
706 | rv(); | |
707 | ~rv(); | |
708 | rv(rv const&); | |
709 | void operator=(rv const&); | |
710 | }; | |
711 | ||
712 | which is convertible to the movable base class (usual C++ derived to base conversion). When users mark | |
713 | their classes as [macroref BOOST_MOVABLE_BUT_NOT_COPYABLE BOOST_MOVABLE_BUT_NOT_COPYABLE] or | |
714 | [macroref BOOST_COPYABLE_AND_MOVABLE BOOST_COPYABLE_AND_MOVABLE], these macros define conversion | |
715 | operators to references to `::boost::rv`: | |
716 | ||
717 | [c++] | |
718 | ||
719 | #define BOOST_MOVABLE_BUT_NOT_COPYABLE(TYPE)\ | |
720 | public:\ | |
721 | operator ::boost::rv<TYPE>&() \ | |
722 | { return *static_cast< ::boost::rv<TYPE>* >(this); }\ | |
723 | operator const ::boost::rv<TYPE>&() const \ | |
724 | { return static_cast<const ::boost::rv<TYPE>* >(this); }\ | |
725 | private:\ | |
726 | //More stuff... | |
727 | ||
728 | [macroref BOOST_MOVABLE_BUT_NOT_COPYABLE BOOST_MOVABLE_BUT_NOT_COPYABLE] also declares a | |
729 | private copy constructor and assignment. [macroref BOOST_COPYABLE_AND_MOVABLE BOOST_COPYABLE_AND_MOVABLE] | |
730 | defines a non-const copy constructor `TYPE &operator=(TYPE&)` that forwards to a const version: | |
731 | ||
732 | #define BOOST_COPYABLE_AND_MOVABLE(TYPE)\ | |
733 | public:\ | |
734 | TYPE& operator=(TYPE &t)\ | |
735 | { this->operator=(static_cast<const ::boost::rv<TYPE> &>(const_cast<const TYPE &>(t))); return *this;}\ | |
736 | //More stuff... | |
737 | ||
738 | In C++0x compilers `BOOST_COPYABLE_AND_MOVABLE` expands to nothing and `BOOST_MOVABLE_BUT_NOT_COPYABLE` | |
739 | declares copy constructor and assigment operator private. | |
740 | ||
741 | When users define the [macroref BOOST_RV_REF BOOST_RV_REF] overload of a copy constructor/assignment, in | |
742 | C++0x compilers it is expanded to a rvalue reference (`T&&`) overload and in C++03 compilers it is expanded | |
743 | to a `::boost::rv<T> &` overload: | |
744 | ||
745 | [c++] | |
746 | ||
747 | #define BOOST_RV_REF(TYPE) ::boost::rv< TYPE >& \ | |
748 | ||
749 | When users define the [macroref BOOST_COPY_ASSIGN_REF BOOST_COPY_ASSIGN_REF] overload, | |
750 | it is expanded to a usual copy assignment (`const T &`) overload in C++0x compilers and | |
751 | to a `const ::boost::rv &` overload in C++03 compilers: | |
752 | ||
753 | [c++] | |
754 | ||
755 | #define BOOST_COPY_ASSIGN_REF(TYPE) const ::boost::rv< TYPE >& | |
756 | ||
757 | As seen, in [*Boost.Move] generates efficient and clean code for C++0x move | |
758 | semantics, without modifying any resolution overload. For C++03 compilers | |
759 | when overload resolution is performed these are the bindings: | |
760 | ||
761 | * a) non-const rvalues (e.g.: temporaries), bind to `::boost::rv< TYPE >&` | |
762 | * b) const rvalue and lvalues, bind to `const ::boost::rv< TYPE >&` | |
763 | * c) non-const lvalues (e.g. non-const references) bind to `TYPE&` | |
764 | ||
765 | The library does not define the equivalent of | |
766 | [macroref BOOST_COPY_ASSIGN_REF BOOST_COPY_ASSIGN_REF] for copy construction (say, `BOOST_COPY_CTOR_REF`) | |
767 | because nearly all modern compilers implement RVO and this is much more efficient than any move emulation. | |
768 | [funcref boost::move move] just casts `TYPE &` into `::boost::rv<TYPE> &`. | |
769 | ||
770 | Here's an example that demostrates how different rlvalue objects bind to `::boost::rv` references in the | |
771 | presence of three overloads and the conversion operators in C++03 compilers: | |
772 | ||
773 | [import ../example/doc_how_works.cpp] | |
774 | [how_works_example] | |
775 | ||
776 | [endsect] | |
777 | ||
778 | ||
779 | [section:thanks_to Thanks and credits] | |
780 | ||
781 | Thanks to all that developed ideas for move emulation: the first emulation was based on Howard Hinnant | |
782 | emulation code for `unique_ptr`, David Abrahams suggested the use of `class rv`, | |
783 | and Klaus Triendl discovered how to bind const rlvalues using `class rv`. | |
784 | ||
785 | Many thanks to all boosters that have tested, reviewed and improved the library. | |
786 | ||
787 | [endsect] | |
788 | ||
789 | [section:release_notes Release Notes] | |
790 | ||
791 | [section:release_notes_boost_1_62 Boost 1.62 Release] | |
792 | ||
793 | * Documented new limitations reported in Trac tickets | |
794 | [@https://svn.boost.org/trac/boost/ticket/12194 #12194 ['"Copy assignment on moveable and copyable classes uses wrong type"]] and | |
795 | [@https://svn.boost.org/trac/boost/ticket/12307 #12307 ['"Copy assignment from const ref handled differently in C++11/C++98"]]. | |
796 | ||
797 | [endsect] | |
798 | ||
799 | [section:release_notes_boost_1_61 Boost 1.61 Release] | |
800 | ||
801 | * Experimental: asymptotically optimal bufferless merge and sort algorithms: [funcref boost::movelib::adaptive_merge adaptive_merge] | |
802 | and [funcref boost::movelib::adaptive_sort adaptive_sort]. | |
803 | ||
804 | * Fixed bug: | |
805 | * [@https://svn.boost.org/trac/boost/ticket/11758 Trac #11758: ['"BOOST_MOVABLE_BUT_NOT_COPYABLE doesn't reset private access with rvalue ref version"]], | |
806 | ||
807 | [endsect] | |
808 | ||
809 | [section:release_notes_boost_1_60 Boost 1.60 Release] | |
810 | ||
811 | * Fixed bug: | |
812 | * [@https://svn.boost.org/trac/boost/ticket/11615 Trac #11615: ['"Boost.Move should use the qualified name for std::size_t in type_traits.hpp"]], | |
813 | ||
814 | [endsect] | |
815 | ||
816 | [section:release_notes_boost_1_59 Boost 1.59 Release] | |
817 | ||
818 | * Changed `unique_ptr`'s converting constructor taking the source by value in C++03 compilers to allow simple conversions | |
819 | from convertible types returned by value. | |
820 | * Fixed bug: | |
821 | * [@https://svn.boost.org/trac/boost/ticket/11229 Trac #11229: ['"vector incorrectly copies move-only objects using memcpy"]], | |
822 | * [@https://svn.boost.org/trac/boost/ticket/11510 Trac #11510: ['"unique_ptr: -Wshadow warning issued"]], | |
823 | ||
824 | [endsect] | |
825 | ||
826 | [section:release_notes_boost_1_58_00 Boost 1.58 Release] | |
827 | ||
828 | * Added [macroref BOOST_MOVE_BASE BOOST_MOVE_BASE] utility. | |
829 | * Added [funcref boost::adl_move_swap adl_move_swap] utility. | |
830 | * Reduced dependencies on other Boost libraries to make the library a bit more lightweight. | |
831 | * Fixed bugs: | |
832 | * [@https://svn.boost.org/trac/boost/ticket/11044 Trac #11044: ['"boost::rv inherits off union, when such passed as template argument"]]. | |
833 | ||
834 | [endsect] | |
835 | ||
836 | [section:release_notes_boost_1_57_00 Boost 1.57 Release] | |
837 | ||
838 | * Added `unique_ptr` smart pointer. Thanks to Howard Hinnant for his excellent unique_ptr emulation code and testsuite. | |
839 | * Added `move_if_noexcept` utility. Thanks to Antony Polukhin for the implementation. | |
840 | * Fixed bugs: | |
841 | * [@https://svn.boost.org/trac/boost/ticket/9785 Trac #9785: ['"Compiler warning with intel icc in boost/move/core.hpp"]], | |
842 | * [@https://svn.boost.org/trac/boost/ticket/10460 Trac #10460: ['"Compiler error due to looser throw specifier"]], | |
843 | * [@https://github.com/boostorg/move/pull/3 Git Pull #3: ['"Don't delete copy constructor when rvalue references are disabled"]], | |
844 | ||
845 | [endsect] | |
846 | ||
847 | [section:release_notes_boost_1_56_00 Boost 1.56 Release] | |
848 | ||
849 | * Added [macroref BOOST_MOVE_RET BOOST_MOVE_RET]. | |
850 | * Fixed bugs: | |
851 | * [@https://svn.boost.org/trac/boost/ticket/9482 #9482: ['"MSVC macros not undefined in boost/move/detail/config_end.hpp"]], | |
852 | * [@https://svn.boost.org/trac/boost/ticket/9045 #9045: ['"Wrong macro name on docs"]], | |
853 | * [@https://svn.boost.org/trac/boost/ticket/8420 #8420: ['"move's is_convertible does not compile with aligned data"]]. | |
854 | ||
855 | [endsect] | |
856 | ||
857 | [section:release_notes_boost_1_55_00 Boost 1.55 Release] | |
858 | ||
859 | * Fixed bugs [@https://svn.boost.org/trac/boost/ticket/7952 #7952], | |
860 | [@https://svn.boost.org/trac/boost/ticket/8764 #8764], | |
861 | [@https://svn.boost.org/trac/boost/ticket/8765 #8765], | |
862 | [@https://svn.boost.org/trac/boost/ticket/8842 #8842], | |
863 | [@https://svn.boost.org/trac/boost/ticket/8979 #8979]. | |
864 | ||
865 | [endsect] | |
866 | ||
867 | ||
868 | [section:release_notes_boost_1_54_00 Boost 1.54 Release] | |
869 | ||
870 | ||
871 | * Fixed bugs [@https://svn.boost.org/trac/boost/ticket/7969 #7969], | |
872 | [@https://svn.boost.org/trac/boost/ticket/8231 #8231], | |
873 | [@https://svn.boost.org/trac/boost/ticket/8765 #8765]. | |
874 | ||
875 | [endsect] | |
876 | ||
877 | [section:release_notes_boost_1_53_00 Boost 1.53 Release] | |
878 | ||
879 | * Better header segregation (bug | |
880 | [@https://svn.boost.org/trac/boost/ticket/6524 #6524]). | |
881 | * Small documentation fixes | |
882 | * Replaced deprecated BOOST_NO_XXXX with newer BOOST_NO_CXX11_XXX macros. | |
883 | * Fixed [@https://svn.boost.org/trac/boost/ticket/7830 #7830], | |
884 | [@https://svn.boost.org/trac/boost/ticket/7832 #7832]. | |
885 | ||
886 | [endsect] | |
887 | ||
888 | [section:release_notes_boost_1_51_00 Boost 1.51 Release] | |
889 | ||
890 | * Fixed bugs | |
891 | [@https://svn.boost.org/trac/boost/ticket/7095 #7095], | |
892 | [@https://svn.boost.org/trac/boost/ticket/7031 #7031]. | |
893 | ||
894 | [endsect] | |
895 | ||
896 | [section:release_notes_boost_1_49_00 Boost 1.49 Release] | |
897 | ||
898 | * Fixed bugs | |
899 | [@https://svn.boost.org/trac/boost/ticket/6417 #6417], | |
900 | [@https://svn.boost.org/trac/boost/ticket/6183 #6183], | |
901 | [@https://svn.boost.org/trac/boost/ticket/6185 #6185], | |
902 | [@https://svn.boost.org/trac/boost/ticket/6395 #6395], | |
903 | [@https://svn.boost.org/trac/boost/ticket/6396 #6396], | |
904 | ||
905 | [endsect] | |
906 | ||
907 | [endsect] | |
908 | ||
909 | [xinclude autodoc.xml] |