Boost logo

Boost :

From: Howard Hinnant (hinnant_at_[hidden])
Date: 2004-01-21 10:58:32


On Jan 21, 2004, at 10:20 AM, Bronek Kozicki wrote:

> Now I just do
> not know how Howard wants to define both classes :/

Sorry to be vague. Part of the problem is that I have not implemented
a fully functioning move_ptr just the way I want. I posted the
following to another reflector recently. It uses the proposed "&&"
move syntax so it doesn't translate into a boost::move_ptr, but I hope
it better illustrates where I'm heading. It includes the S<T[]>
syntax, and a deletion policy via a second template parameter. I
understand that this may not be how you want to introduce a deletion
policy. But one of the things I like about the design below is that
the overhead can be brought down to zero.

-Howard

-------

Something like:

namespace std
{

namespace detail
{

struct apply_delete
{
     template <class T>
     void operator() (T* ptr) {typedef char incomplete[sizeof(T)];
delete ptr;}
};

struct apply_array_delete
{
     template <class T>
     void operator() (T* ptr) {typedef char incomplete[sizeof(T)];
delete [] ptr;}
};

template <class T, bool b = is_array<T>::value>
struct default_delete
{
     typedef apply_delete type;
};

template <class T>
struct default_delete<T, true>
{
     typedef apply_array_delete type;
};

} // detail

template<class T, class D = typename detail::default_delete<T>::type>
class move_ptr
{
public:
     typedef T element_type;
     typedef D deleter_type;
     typedef ... deleter_reference;
     typedef ... deleter_const_reference;

     explicit move_ptr(T* p = 0);
     move_ptr(T* p, deleter_const_reference d);

     // enable move from rvalue
     move_ptr(move_ptr&& a);

     template <class U, class E>
     move_ptr(move_ptr<U, E>&& a);

     move_ptr& operator=(move_ptr&& a);

     template <class U, class E>
     move_ptr& operator=(move_ptr<U, E>&& a);

     ~move_ptr();

     T& operator*() const throw();
     T* operator->() const throw();
     T* get() const throw();
     T* release() throw();
     void reset(T* p = 0);
     void swap(move_ptr&& a)
     deleter_reference get_deleter();
     deleter_const_reference get_deleter() const;

     operator int bool_type::*() const;

private:

     // disable copy from lvalue
     move_ptr(const move_ptr& a);
     template <class U, class E> move_ptr(const move_ptr<U, E>& a);
     move_ptr& operator=(const move_ptr& a);
     template <class U, class E> move_ptr& operator=(const
move_ptr<U, E>& a);
};

template<class T, class D>
class move_ptr<T[], D>
{
public:
     typedef T element_type;
     typedef D deleter_type;
     typedef ... deleter_reference;
     typedef ... deleter_const_reference;

     explicit move_ptr(T* p = 0);

     move_ptr(T* p, deleter_const_reference d);

     // enable move from rvalue
     move_ptr(move_ptr&& a);
     move_ptr& operator=(move_ptr&& a);

     ~move_ptr();

     T& operator[](size_t i) const;
     T* get() const throw();
     T* release() throw();
     void reset(T* p = 0);
     void swap(move_ptr&& a);
     deleter_reference get_deleter();
     deleter_const_reference get_deleter() const;

     operator int bool_type::*() const;

private:
     // disable copy from lvalue
     move_ptr(const move_ptr& a);
     move_ptr& operator=(const move_ptr& a);
};

} // std

Notes:

move_ptr behaves as described in:

http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/
n1377.htm#move_ptr%20Example

It is movable but not copyable. It has a custom deleter. The deleter
can have reference type. This is handy when you want to create a
move_ptr with a deleter that refers to state outside of the smart
pointer, e.g.

    MyDeleter del;
    move_ptr<T, MyDeleter&> p(new T, del);

Deleter state can be directly referenced via const and non-const
get_deleter() functions.

There are two move_ptr forms: one for array and one for non-array.
The interfaces for the two forms differ slightly to accommodate the
different use cases (array vs non-array). The non-array form is used
like:

move_ptr<int> p1(new int);

The array form looks like:

move_ptr<int[]> p2(new int[3]);

The non-array form handles derived/base conversions (like auto_ptr) and
most of the other familiar auto_ptr-like member functions. The array
form does not do derived/base conversions. It lacks operator->() and
operator*() but has operator[](size_t).

A quality implementation would have the same overhead as auto_ptr for
"empty" (or stateless deleters - like the default), say by using
boost::compressed_pair<T*, D> to store the two data members.

-Howard


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk