...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
As previously mentioned, Boost.Intrusive containers are non-copyable and non-assignable, because intrusive containers don't allocate memory at all. To implement a copy-constructor or assignment operator, the user must clone one by one all the elements of the container and insert them in another intrusive container. However, cloning by hand is usually more inefficient than a member cloning function and a specialized cloning function can offer more guarantees than the manual cloning (better exception safety guarantees, for example).
To ease the implementation of copy constructors and assignment operators of
classes containing Boost.Intrusive containers,
all Boost.Intrusive containers offer a special
cloning function called clone_from
.
Apart from the container to be cloned, clone_from
takes two function objects as arguments. For example, consider the clone_from
member function of list
:
template <class Cloner, class Disposer> void clone_from(const list &src, Cloner cloner, Disposer disposer);
This function will make *this
a clone of src
. Let's explain
the arguments:
value_type
objects and return a pointer
to the clone. It must implement the following function: pointer
operator()(const value_type
&)
.
value_type
objects. It's used first to
empty the container before cloning and to dispose the elements if an exception
is thrown.
The cloning function works as follows:
Here is an example of clone_from
:
#include <boost/intrusive/list.hpp> #include <iostream> #include <vector> using namespace boost::intrusive; //A class that can be inserted in an intrusive list class my_class : public list_base_hook<> { public: friend bool operator==(const my_class &a, const my_class &b) { return a.int_ == b.int_; } int int_; //... }; //Definition of the intrusive list typedef list<my_class> my_class_list; //Cloner object function struct new_cloner { my_class *operator()(const my_class &clone_this) { return new my_class(clone_this); } }; //The disposer object function struct delete_disposer { void operator()(my_class *delete_this) { delete delete_this; } }; int main() { const std::size_t MaxElem = 100; std::vector<my_class> nodes(MaxElem); //Fill all the nodes and insert them in the list my_class_list list; for(std::size_t i = 0; i < MaxElem; ++i) nodes[i].int_ = (int)i; list.insert(list.end(), nodes.begin(), nodes.end()); //Now clone "list" using "new" and "delete" object functions my_class_list cloned_list; cloned_list.clone_from(list, new_cloner(), delete_disposer()); //Test that both are equal if(cloned_list != list) std::cout << "Both lists are different" << std::endl; else std::cout << "Both lists are equal" << std::endl; //Don't forget to free the memory from the second list cloned_list.clear_and_dispose(delete_disposer()); return 0; }