Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. — Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

Cloning Boost.Intrusive containers
PrevUpHomeNext

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:

  • The first parameter is the list to be cloned.
  • The second parameter is a function object that will clone value_type objects and return a pointer to the clone. It must implement the following function: pointer operator()(const value_type &).
  • The second parameter is a function object that will dispose 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:

  • First it clears and disposes all the elements from *this using the disposer function object.
  • After that it starts cloning all the elements of the source container using the cloner function object.
  • If any operation in the cloning function (for example, the cloner function object) throws, all the constructed elements are disposed using the disposer function object.

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 int MaxElem = 100;
   std::vector<my_class> nodes(MaxElem);

   //Fill all the nodes and insert them in the list
   my_class_list list;

   for(int i = 0; i < MaxElem; ++i) nodes[i].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;
}

PrevUpHomeNext