![]() |
Home | Libraries | People | FAQ | More |
boost::movelib::unique_ptr
is a smart pointer with exclusive ownership, equivalent to std::unique_ptr.
It owns an object through a pointer and deletes the object when the unique_ptr is destroyed. A unique_ptr can be moved but it can not be
copied, so only one unique_ptr
owns a given object. It works with C++03 compilers, where the move semantics
are emulated by Boost.Move, and with C++11
and later compilers, where it uses rvalue references.
The library has three headers:
boost/move/unique_ptr.hpp:
the class template unique_ptr.
boost/move/default_delete.hpp:
the default deleter default_delete.
unique_ptr.hpp includes it.
boost/move/make_unique.hpp:
the creation functions (make_unique
and others). It is a separate header because in C++03 it uses the preprocessor
to emulate variadic templates, and this makes the compilation slower.
All the names are in the namespace boost::movelib.
The examples use this namespace alias:
namespace bml = ::boost::movelib;
The interface is the same as the interface of std::unique_ptr:
nullptr constructors make an empty unique_ptr.
unique_ptr
becomes empty.
get()
returns the stored pointer, operator* and operator-> give access to the object and explicit operator
bool is true if the unique_ptr owns an object.
reset(p)
deletes the owned object and takes ownership of p.
release()
returns the pointer and the unique_ptr
does not own the object after the call.
swap exchanges the pointers
and the deleters of two unique_ptr
objects.
nullptr.
#include <boost/move/unique_ptr.hpp> #include <boost/move/make_unique.hpp> namespace bml = ::boost::movelib; class widget { public: explicit widget(int v) : value(v) {} int value; }; //A factory: the caller owns the returned object bml::unique_ptr<widget> make_widget(int v) { return bml::make_unique<widget>(v); } //A sink: the function takes the ownership of the object int consume_widget(bml::unique_ptr<widget> w) { return w->value; } //The widget is deleted here void basic_example() { bml::unique_ptr<widget> p = make_widget(1); //p owns a widget assert(p && p->value == 1); bml::unique_ptr<widget> q(boost::move(p)); //The ownership goes from p to q assert(!p && q->value == 1); q.reset(new widget(2)); //Deletes the first widget assert(q->value == 2); widget *raw = q.release(); //q does not own the widget now assert(!q); delete raw; q = make_widget(3); assert(consume_widget(boost::move(q)) == 3); //The sink owns and deletes it assert(!q); }
In C++03 compilers, write the code as in the example:
boost::move to transfer the ownership from
a named unique_ptr. A
temporary (for example, the result of a function) is moved without boost::move.
unique_ptr
with return boost::move(local); or with return
BOOST_MOVE_RET(bml::unique_ptr<T>, local);
(see Implicit Move when returning a
local object). A C++11 compiler does the move without these.
unique_ptr
by value. A function that can take the ownership, but does not always
do so, can take a BOOST_RV_REF(bml::unique_ptr<T>) parameter.
0 can be used where
C++11 code uses nullptr,
for example in p =
0;.
unique_ptr<T[]>
owns an array created with new[], and its default deleter uses delete[].
The interface for arrays has these differences:
operator[]
gives access to the elements. operator* and operator-> are not available.
reset
do not accept a pointer to a derived type, because the element size of
the array would be incorrect. They accept a pointer to a less cv-qualified
type (for example, unique_ptr<const T[]>
takes a T*).
void array_example() { //new[] is paired with delete[] bml::unique_ptr<int[]> a(new int[3]); a[0] = 1; a[1] = 2; a[2] = 3; assert(a[1] == 2); //Value-initialized elements: all are zero bml::unique_ptr<int[]> z = bml::make_unique<int[]>(10); assert(z[0] == 0 && z[9] == 0); //Default-initialized elements: the values are not set bml::unique_ptr<int[]> d = bml::make_unique_definit<int[]>(10); d[0] = 1; assert(d[0] == 1); }
As an extension, unique_ptr<T[N]>
(an array of known bound) is also an array unique_ptr:
it owns the result of new T[N],
operator[]
asserts that the index is less than N,
and unique_ptr<T[N]> converts to unique_ptr<T[]>.
std::unique_ptr<T[N]> is not an array unique_ptr:
it uses the single-object interface, so it can not take ownership of new T[N].
boost/move/make_unique.hpp
has these functions. They return a unique_ptr
that owns the new object. It is not necessary to write new,
and there is no memory leak if an exception is thrown between the allocation
and the construction of the unique_ptr.
|
Function |
Creates |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
definit functions
are equivalent to C++20's std::make_unique_for_overwrite.
Use them when the values are set after the construction, because they
do not set the values of trivial types. make_unique_for_overwrite<T>() and make_unique_for_overwrite<T[]>(n) are also available, for compatibility.
nothrow functions
do not throw std::bad_alloc. If the allocation fails,
they return an empty unique_ptr.
T[N]).
The second template parameter D
of unique_ptr<T, D> is the deleter: the function object that
unique_ptr calls to destroy
the owned object. The default is default_delete<T>,
which calls delete (or delete[] for
arrays). A custom deleter can release other resources, for example a handle
of a C library:
//A C-style interface that creates and destroys a resource struct resource { int handle; }; int destroyed_resources = 0; resource *open_resource(int h) { resource *r = new resource; r->handle = h; return r; } void close_resource(resource *r) { ++destroyed_resources; delete r; } //A deleter without state: it adds no size to unique_ptr struct resource_closer { void operator()(resource *r) const { close_resource(r); } }; void deleter_example() { { bml::unique_ptr<resource, resource_closer> r(open_resource(1)); assert(r->handle == 1); //The empty deleter uses no storage assert(sizeof(r) == sizeof(resource*)); } //close_resource is called here assert(destroyed_resources == 1); { //A function pointer as deleter: it is stored in the unique_ptr bml::unique_ptr<resource, void(*)(resource*)> r(open_resource(2), &close_resource); assert(r.get_deleter() == &close_resource); } assert(destroyed_resources == 2); { //A reference to a deleter: the unique_ptr uses the deleter object of the caller resource_closer closer; bml::unique_ptr<resource, resource_closer&> r(open_resource(3), closer); assert(&r.get_deleter() == &closer); } assert(destroyed_resources == 3); }
unique_ptr uses the deleter object
of the caller, and get_deleter() returns that reference.
sizeof(unique_ptr<T, D>) is equal to sizeof(T*). A final
or union deleter is stored
as a data member.
D has a nested type
D::pointer, it is the type of the stored
pointer (unique_ptr<T, D>::pointer), otherwise the stored pointer
is T*.
This makes it possible to own a resource that is not identified by a
raw pointer, for example a handle type that satisfies the requirements
of NullablePointer.
A unique_ptr<U, E> rvalue converts to a unique_ptr<T,
D>
if U*
converts to T*
and the deleter E converts
to D. For arrays, the element
types must be the same except for the cv-qualification:
struct base { base() : id(0) {} virtual ~base() {} int id; }; struct derived : base { derived() { id = 1; } }; void conversion_example() { bml::unique_ptr<derived> d = bml::make_unique<derived>(); //A unique_ptr to a derived class converts to a unique_ptr to a base class bml::unique_ptr<base> b(boost::move(d)); assert(!d && b->id == 1); //A unique_ptr to an array converts to a unique_ptr to an array of more cv-qualified elements bml::unique_ptr<int[]> a(new int[2]); bml::unique_ptr<const int[]> ca(boost::move(a)); assert(!a && ca); }
When the object is deleted through a pointer to a base class, the base class
must have a virtual destructor. When the compiler gives the necessary information
(a has_virtual_destructor
intrinsic), Boost.Move detects this error
at compile time: a unique_ptr
with a default_delete does
not compile if a pointer to a derived class is converted to a pointer to
a base class without a virtual destructor. std::unique_ptr
compiles this code, and the program has undefined behavior.
struct no_virtual_dtor_base {}; struct derived2 : no_virtual_dtor_base {}; bml::unique_ptr<no_virtual_dtor_base> p(new derived2); //Compilation error
A unique_ptr can be stored
in a container that supports move semantics. In C++03 compilers, the standard
containers do not support move semantics, so use the Boost.Container
containers:
#include <boost/container/vector.hpp> void container_example() { boost::container::vector< bml::unique_ptr<widget> > v; v.push_back(bml::make_unique<widget>(1)); //A temporary is moved bml::unique_ptr<widget> w = bml::make_unique<widget>(2); v.push_back(boost::move(w)); //A named object must be moved explicitly assert(v.size() == 2 && !w); assert(v[0]->value == 1 && v[1]->value == 2); } //The vector deletes the widgets
nullptr constructor are constexpr. So a unique_ptr
object with static storage duration is constant-initialized, and it is
not affected by the order of dynamic initialization.
constexpr
destructors and dynamic memory allocation in constant expressions, all
the operations of unique_ptr
and default_delete and
the functions make_unique
and make_unique_definit
are constexpr. This is the
same constexpr support as
C++23's std::unique_ptr, but it is available in
C++20. The nothrow creation
functions are not constexpr.
constexpr bool test() { bml::unique_ptr<int> p = bml::make_unique<int>(1); p.reset(new int(2)); return *p == 2; } //p deletes the int in the constant evaluation static_assert(test(), "");
When the compiler supports the trivial_abi
attribute (Clang targeting the Itanium C++ ABI), unique_ptr
has this attribute. Then, if the pointer is a raw pointer and the deleter
is trivially copyable, the compiler passes a unique_ptr
to a function and returns it from a function in registers, as it does for
a raw pointer.
This changes the calling convention of functions that take or return a unique_ptr by value. All the translation
units that pass a unique_ptr
between them must use the same convention. To keep the previous calling convention,
define BOOST_MOVE_DISABLE_TRIVIAL_ABI
in all of them.
Improvements
constexpr
in C++20, but std::unique_ptr is constexpr
only from C++23 (see constexpr
support).
make_unique is available
in C++03 and C++11 (std::make_unique
is C++14), and make_unique_for_overwrite
(also named make_unique_definit)
is available before C++20. The nothrow
creation functions have no standard equivalent (see Creation
functions).
unique_ptr<T[N]>. In Boost.Move,
unique_ptr<T[N]>
is an array unique_ptr
that owns a T*,
and operator[]
asserts that the index is less than N
(see Arrays).
std::unique_ptr
the program has undefined behavior (see Conversions).
unique_ptr
has the trivial_abi attribute
by default, so it is passed and returned in registers. The standard library
implementations do not do this by default (see Calling
convention).
<cassert> and <cstddef>
from the standard library. std::unique_ptr
needs <memory>, which is a large header.
Other differences
<,
<=, >
and >=) of two unique_ptr objects use the < operator of the pointers, not std::less. This avoids a dependency on the
<functional> and <type_traits>
headers. On all the platforms supported by Boost, the <
operator of raw pointers gives a strict weak order.
std::hash specialization.
operator<<) takes the stream type as a template
parameter, so unique_ptr.hpp
does not include <iosfwd>. It participates in overload resolution
only for std::basic_ostream and the classes derived
from it, and it returns the stream with its own type.
boost::movelib::unique_ptr and std::unique_ptr
are different types. They do not convert to each other, but ownership
can be transferred with release().