smart_ptr - Smart Pointer Containers

Introduction

A smart pointer is a class that acts like a pointer but which also deletes the object pointed to when finished. It is, if you like, a memory-managing pointer.

A smart pointer maintains a pointer to a single object. It also keeps a count of how many smart pointers point to the object. When smart pointers are assigned or 'copied', no copying of the object pointed to takes place, instead the pointer count is just incremented. This is known as aliasing. When a destructor is called, the count is just decremented. This is known as dealising. The object itself is destroyed when the count is decremented to zero, indicating that the last alias has been destroyed.

There's lots of reasons for using smart pointers in C++ and I now find it hard to remember how I managed without. Or why I didn't write this component many years earlier than I did. Lots of hours, days, weeks even months have been blown away uselessly in debugging of memory problems, running memory diagnostic tools (even writing one) and generally squandering effort on problems which were, in hindsight, a product of a bad programming style.

C++ novices don't at first understand what possible use smart pointers are - don't you just delete a pointer when you've finished with it? This is a naive viewpoint as I hope will become clear in the explanation that follows.

Here's my top 5 reasons for using smart pointers:

Reason 1 - C++ Polymorphism Must be Implemented as Pointers to Objects

In C++, you manipulate families of derived classes through a pointer to the base class. You can then access virtual functions by dereferencing the baseclass pointer. For example

class base
{
  ...
  virtual void print(void) const;
};

class derived : public base
{
  ...
  virtual void print(void) const;
};

...

base* b = new derived;
b->print(); // actually calls derived::print

The reason that you must use pointers here is that classes base and derived could be of different sizes - so you cannot assign a derived to a variable of type base. This rule is enforced by C++.

However, the use of pointers introduces a potential problem - the pointer is unmanaged. That is, the memory it points to will not be recovered when it goes out of scope. Okay, so you can simply delete it, but that doesn't solve the problem of what to do if an exception is thrown. You could have a catch blcok in every single function you write that allocates memory - but then you've written a lot of code that in practice cannot be tested. It is good C++ practice to make all variables self-destroying so that they destroy themselves if an exception is thrown. Then you don't need catch blocks. Not only does making all variables self-destroying make for cleaner programming, but it is the only realistic way of making your code exception-safe.

The solution to all this is to use smart pointers (the cloning variant in the case of polymorphic types) rather than simple pointers. Then the memory is managed by the smart pointer - if the pointer goes out of scope either because rthe function returns or because an exception is thrown, then all its contents will be destroyed correctly.

Reason 2 - Using Pointers to Objects in STL Container Classes

Now look at an even more typical problem. In this case you want to store pointers to objects in an STL container for whatever reason. The problem is that this means that the vector no longer acts as a true container, because if the vector is cleared or goes out of scope or elements are removed, the pointers will not be deleted. You have just written a leaky program. Trying to keep track of these pointers and delete them yourself every time you change the vector is terribly clumsy and error prone and is definitely not pukka C++. You see, the vector is supposed to be a container class - it should manage the memory of the objects it contains. However, using simple pointers breaks the container nature of the component.

The solution is to use smart pointers rather than simple pointers. Then the memory is managed by the smart pointer - if the STL container goes out of scope then all its contents will be destroyed correctly.

Reason 3 - Minimising Reallocation Overhead

The vector is one of the most useful template classes in the STL. It has one potential problem which totally freaks out some programmers, but not for a good reason. The problem is that, when a vector has to resize, it does so by copying all of its contents into a new vector. It does so by using the copy constructor of the contained type. Therefore a vector of 1000 objects results in 1000 calls to the copy constructor. This is not itself a problem if the constructor is trivial since the call overhead is negligible.

However, if the type stored in the vector is big and/or complicated, then the copy overhead can become serious. Storing a pointer to the object in the vector is not a good idea - see Reason 2 above. The solution is to use a vector of smart pointers to objects. Then, when the vector resizes, it copies the smart pointer, not the object. The overhead of copying a smart pointer is negligible - only slightly more than a simple pointer.

Problem solved.

Reason 4 - Preserving the Object's Memory Address

Another similar problem is that, if you keep an object in an STL vector and the vector reallocates for any reason, then the object changes its position in memory and any pointer to the old object no longer points to it.

Adding a level of indirection using a smart-pointer solves this problem, because now we have the behaviour of a pointer in that it is the smart pointer class that gets moved in memory, not the object that it contains, but we also have the memory-management of an object so there is no requirement for the user to manage the memory of the object.

Reason 5 - Returns Must be Implemented as Pass-by-Value

It is often useful to write functions that create data structures and return them. However, there are problems with this.

Passing back a reference is illegal if the function created the object as an automatic (local) variable, since that is destroyed in the return so that the reference returned now refers to destroyed memory. This is a well-known pitfall in C++.

Creating the object with new and passing back a pointer to the object seems to be a good solution and indeed it is a sort-of solution. However, it is a bodged solution, not a real one. The problem with this is that the returned value is not memory managed and will leak in the case of an exception being thrown. It can also make code difficult to read and debug because the place where the data structure is allocated is a very different place to where it is destroyed. This is a well-known source of memory bugs - inevitably the question of which part of the program is responsible for the deallocation becomes confused and you can end up with either leaky code or double-deletion and the memory corruption that results.

If you return by value, this causes a copy constructor to be called. This is fine for small data structures where the copy overhead is correspondingly small, but it is undesirable if the data staructure is big and impossible if the data structure cannot be copied (occasionally it is necessary to design data structures that are not copiable).

The solution is to create the object in a smart pointer and return it by value. Pass by value with a smart pointer just creates an alias and so has negligible overhead. However, the smart pointer is managing the object's memory, so the program does not need to remember to delete it later. It will delete itself when it is no longer in use.

Summary

I think the days of tracking dynamic memory lifetimes through layers of software and running leakage diagnostics in programs is now well past. It belongs in the 80s along with the Commodore Pet and the Sinclair QL. Debugging double-deletion problems is an absolute nightmare and totally unnecessary when it is possible and practical to make every data structure self-managing.

The gist of my argument is that we live in more sophisticated and enlightened times. It is no longer necessary to do any memory allocation or deallocation yourself - all memory can and should be allocated implicitly by container classes.

If I dare to say so, you end up programming in C++ in a similar style to Java. The advantage is that no garbage collection is needed because objects are deleted properly when they go out of scope rather than drifting off in the vain hope that the garbage collector might eventually spot it. Ten bonus points to C++ I think.

The STL doesn't deal with this issue (well, it does have a memory managing pointer class called auto_ptr which is a partial solution, but its pretty crap. Come to think of it, totally crap would be a better verdict). Thus the smart pointer classes are my solution to the issue. For the last two years the only time I've written calls to delete are when designing the container classes in STLplus and the only calls to new are in the same classes or when when supplying the clone() functions required when using polymorphic types with smart_ptr_clone (more later). There is not one single call to delete anywhere in over a megabyte of application code that I've written in that time. There is not a single memory leak and it has been well over two years at the time of writing since I had any memory leaks in any software I have written. It is truly a thing of the past.

What Are Smart Pointers?

A smart pointer is basically a pointer with a destructor. The destructor ensures that the object pointed to is deleted when it is no longer being used. You can have multiple pointers to the same object, so the object is only deleted when the last pointer is destroyed.

A smart pointer has to have slightly different characteristics to a conventional C pointer. These differences show up in the behaviour of the pointer on assignment and when the pointer's address (i.e. the object being pointed to) is changed. Deciding the exact behaviour is difficult and very subjective, and its impossible to design a single pointer class that suits every application, so the solution is to have a range of different classes with slightly different behaviour. You can then choose which one suits your problem.

There are two kinds of smart pointer defined in the smart_ptr.hpp header. These have two distinct ways of handling assignment and changes to the address pointed to. However, both have some common behaviour so I will describe that first.

The smart pointer contains the address of the object pointed to and a counter which stores the alias count. When one smart pointer is assigned to another, the second smart pointer becomes an alias of the first, which means that it is pointing to the same object. The alias count of the object is incremented to show that there are now two pointers pointing to the same object.

Meanwhile, the object originally pointed to by the second pointer is dealiased. This means that the smart pointer no longer points to it. It's alias count is decremented to show that one less smart pointer is pointing at the object. When the alias count decrements to zero, this indicates that the object is no longer being pointed to by any smart pointers and so it is deleted automatically.

Simple Pointers - class simple_ptr

Class simple_ptr is a smart pointer that behaves most like a conventional pointer in C. It contains a pointer to the contained object and a second pointer to a separate object containing the alias count. This is illustrated in Figure 1:


Figure 1: The structure of class simple_ptr

In this figure, there are two simple_ptr objects pointing to the same data object. Therefore they both also point to the same alias count, which contains the value 2 since there are two simple_ptr objects pointing to it.

A consequence of this design is that if you change the address of the object pointed to by one simple_ptr, you must dealias first. In other words, if you change the address of one simple_ptr, you cannot know where the other simple_ptr objects are, so you cannot change them too. Thus the other simple pointers must be left pointing to the old object whilst the changed simple pointer points to the new object. In fact, the dealiasing is done automatically by the simple_ptr class.

This behaviour is very like conventional C pointers - if you allocate an address to a pointer variable, you don't expect other pointer variables to change too. However, it can have disadvantages in data structure design. This is why the second variant - the smart_ptr class was designed.

Smart Pointers - class smart_ptr

Class smart_ptr is a smart pointer that behaves unlike a conventional pointer in C. It was the first class designed, which is why it is called smart_ptr, leading to a confusion between the term "smart pointer" meaning the general concept of smart pointers and the use of the term to indicate this specific variant. This confusion is because I did not at the time needing different kinds of smart pointer.

A smart pointer class contains a pointer to a sub-object which in turn contains the address of the contained object and an integer containing the alias count. This is illustrated in Figure 2:


Figure 2: The structure of class smart_ptr

In this figure, there are two smart_ptr objects pointing to the same sub-object, which then points to the data object. The alias count is stored in the sub-object. There is an alias count of 2 because there are two smart_pointer objects pointing to the sub-object.

A consequence of this design is that if you change the address of the object pointed to by one smart_ptr, you do not have to dealias first. In other words, if you change the address of one smart_ptr, you are actually changing the address stored in the sub-object, which is visible to all other aliases, so these change their stored address too. The result is that all aliases end up still being aliases and all pointing to the new object, whilst the old object gets deleted automatically because it is no longer pointed to.

This behaviour is unlike conventional C pointers - if you allocate an address to a pointer variable, you don't expect other pointer variables to change too. However, it can have very significant advantages in data structure design. It is common when designing large and complex data structures to have several different ways of accessing objects - for example you might store an object in a map which allows access to objects sorted in alphabetical order, but also store it in a vector which allows access to objects in the order in which they were created. Traditionally this could lead to problems in memory managing the object when removing it from one or other since it could become ambiguous which data structure is the container responsible for deallocating its memory. However, with smart pointers this problemn disappears - put a smart pointer in both map and vector pointing to the same object, i.e. alises. The object will persist until both smart pointers are deallocated. Furtermore, if the object pointed to needs to be changed, it can be changed by changing either alias. So, you can look it up in the map, change the address and the address pointed to by the other alias in the vector will automatically change to the new object - they remain consistent.

This is the thinking behind the smart pointer design, even though it seems strange and a bit counter-intuitive to people unused to using smart pointers. In my opinion the power of the design outweighs the slight strangeness.

Smart Pointer Variants

Each of the two kinds of smart pointer described in the last section has three variants, making 6 classes altogether. The variants differ in their ability to create copies of the object pointed to.

For example, smart_ptr has these variants:

template<typename T> class smart_ptr
This is a smart pointer intended for use on simple types and single classes. It uses the copy constructor to copy the object of type T that it contains.
template<typename T> class smart_ptr_clone
This is a smart pointer intended for use on polymorphic types - that is families of classes and subclasses which are manipulated through a pointer to their superclass. It uses a clone() function to copy objects.
template<typename T> class smart_ptr_nocopy
This is a smart pointer intended for use on objects that cannot be copied - for example a class that contains a system resource other than memory where copying makes no sense, or a data structure too complex to implement a copy operation in a reasonable time-frame. It is like smart_ptr but has all operations that cause copying of the pointed-to object stripped out.

Although there are six smart pointer classes, they have almost identical interfaces, so they are described together here. I use the most common variant - smart_ptr - as the basis of this description and then describe the differences with the variants at the end.

Constructing and Destroying

There are four constructors and of course a destructor for smart_ptr:

smart_ptr::smart_ptr(void);
explicit smart_ptr::smart_ptr(T* data);
smart_ptr::smart_ptr(const T& data);
smart_ptr::smart_ptr(const smart_ptr<T>& r);
smart_ptr::~smart_ptr(void);

The first of the constructors simply creates a null pointer (it is perfectly legal and correct to have a null smart_ptr).

The second constructs a smart pointer from a pointer to an object. This pointer must be created using new (if you pass the address of an automatic variable, things will go horribly wrong). For example:

smart_ptr<string> s1 = new string("Hello World");

The third constructs a pointer from an object. Note however that the smart_ptr copies the object passed in this way. It does not manage the original object. This is because the smart_ptr must contain an object created dynamically (on the heap) using new, and ensures this by doing the allocation itself.

The final form of constructor is the copy constructor - bit it doesn't copy the contents of the pointer. Instead it implements the aliasing behaviour of the smart pointer classes.

The destructor implements the other half of the aliasing behaviour. When a smart pointer is destroyed, the alias count is decremented. If this makes it zero, then the object contained by the smart pointer is deleted.

A simple example illustrates how a pointer is constructed and initialised in a single step:

smart_ptr<string> s1(string("testing"));

In fact, because the string class has an implicit type conversion from char*, it can be written more simply than this:

smart_ptr<string> s2("testing");

Here's another example which constructs two smart pointers to point to the same string:

string s = "testing";
smart_ptr<string> s1 = s;
smart_ptr<string> s2 = s1;

This example takes advantage of the C++ convention that an initialisation expressed as an assignment is in fact a call to the constructor. The first line creates a string initialised with the value "testing". The second line creates a smart pointer initialised with a copy of this (remember that the object contained by the smart pointer is copied into it). The third line creates another smart pointer which is an alias of the first one. Note that the second smart pointer is initialised from the first smart pointer, meaning they point to the same object.

Since both s1 and s2 point to the same string, modifying the string though s1 will result in the string pointed to by s2 apparently changing. However, the string in s1 and s2 is a copy of the original string s, so changing s will not change s1 or s2.

Typically the object is constructed as an anonymous temporary in the call to the smart_ptr constructor:

smart_ptr<string> s1(string("testing"));

Note that an anonymous temporary object can be constructed by using the class name (in this case 'string') as if it was a function call. This is a common convention in 21st century C++.

Assignments

As is the usual convention with C++ classes, there is an assignment operator corresponding to each constructor with equivalent behaviour (except of course for the void constructor since C++ doesn't allow the use of void as a value).

First, there is an assignment that allows the object pointed to by smart_ptr to be replaced by a new-allocated object:

smart_ptr<T>& smart_ptr::operator=(T* data);

So for example, you can write:

s1 = new string("Hello World");

Once again you must allocate this with new. If you pass a pointer to an automatic variable, the smart pointer will later try to delete it. Similarly, don't pass the address of an object already contained in another data type - both containers will try to delete it.

The purpose of this function is to allow an object to be alloced and then handed over to the smart pointer to be managed by it without incurring the overhead of a copy operation. Assigning an object (rather than a pointer to the object) to a smart pointer causes a copy of the object to be made (see just below). This is fine for simple types and data structures since the copy overhead is small. For large data structures however, the copy overhead can become excessive. Furthermore some objects cannot be copied. In this case it is more efficient to use pointer assignment for this. However, it is potentially dangerous in that if an invalid address is passed in (for example the address of an automatic variable) then all hell will break loose.

Note: It is your fault if you get caught out by this. I have warned you to be careful!

Note 2: I seriously considered removing the T* constructor and assignment because of the possibility of it being abused. However, initialising a smart pointer this way avoids copying the object - and in some cases this is essential either because the object is very big or is designed to be uncopyable (i.e. has no copy constructor or assignment operator available). So just be careful.

When you assign to a smart pointer in this way, not only that smart pointer but all aliases of it end up pointing to the new object.

Furthermore, assigning a null pointer will clear the smart pointer and all of its aliases.

smart_ptr<T>& smart_ptr::operator=(const T& data);

This assignment of the contained type T to the smart pointer changes the value pointed to by this smart pointer and of all other pointers that are aliases to it. It deletes the old value and then creates a new object which is a copy of the parameter.

For example:

s1 = "Hello World";

This is slightly strange behaviour - you are assigning a string to a smart_ptr<string>! With simple pointers this would be illegal, but smart pointers allow it. The old value of the string will be discarded and the new value copied into its place. It is approximately equivalent to dereferencing the pointer and assigning to the string itself:

*s1 = "Hello World";

There's a final form of assignment:

smart_ptr<T>& smart_ptr::operator=(const smart_ptr<T>&);

This assignment of a smart pointer to another dealiases this smart pointer from any other aliases and deletes the contents if this was the last alias to it. It then makes this smart pointer a alias to the parameter.

Setting a simple pointer

void smart_ptr::set(T* data = 0);

This deletes the previous pointer and adopts the passed pointer instead. Note: the object passed must be allocated by the user with new. The default value means that calling set with no argument makes the pointer null.

In fact this function is functionally identical to the assignment operator:

smart_ptr<T>& smart_ptr::operator=(T* data);

It is present in the class because some people prefer its more explicit form.

When you assign to a smart pointer in this way, not only that smart pointer but all aliases of it end up pointing to the new object.

Logical Tests

As the previous section showed, it is possible for a pointer to be null. Of course, it is therefore necessary to be able to test for a null pointer. No problem. The smart pointer has a set of four test functions, tests for null and non-null in two forms; explicit (function) form and implicit (operator) form. The implicit (operator) form is provided by the following operators:

smart_ptr::operator bool(void) const;
bool smart_ptr::operator!(void) const;

The following examples show how to use these:

if (s1)
  <non-null handler>
else if (!s2)
  <null handler>

Alternatively, use the explicit (function) form:

bool smart_ptr::present(void) const;
bool smart_ptr::null(void) const;

In use:

if (s1.present())
  <non-null handler>
else if (s2.null())
  <null handler>

Dereferencing Operators

The smart pointer classes look quite like pointers in use.

To dereference a smart pointer, simply use the * operator just like a simple pointer. In fact there are two variants of this:

T& smart_ptr::operator*(void) throw(null_dereference);
const T& smart_ptr::operator*(void) const throw(null_dereference);

In other words, if you dereference a non-const smart_ptr you get a non-const reference to the object which you can then read from or write to. If you dereference a const smart_ptr, you get back a const reference which is therefore read-only.

Dereferencing a null smart pointer causes the null_dereference exception to be thrown. Therefore if you are unsure whether a smart pointer is null, use one of the tests above to check first.

Similarly, just as with simple pointers, to dereference and access a member in one go, use the -> operator. There are two forms of this too:

T* smart_ptr::operator->(void) throw(null_dereference);
const T* smart_ptr::operator->(void) const throw(null_dereference);

So, if you dereference a non-const smart_ptr you get a pointer to a non-const object and therefore can call non-const methods. If you dereference a const smart_ptr you get back a pointer to a const object and therefore can only call const methods.

These operators are strange because they appear to just return a simple pointer - in fact the language forces you to use them as a prefix to a method call or member access. Because of this it is illegal to use them to access a null pointer and the same exception is thrown as with operator*.

There are functions with the same behaviour as these operators, to provide a more explicit access to the pointer or value pointed to:

void smart_ptr::set_value(const T& data);
T& smart_ptr::value(void) throw(null_dereference);
const T& smart_ptr::value(void) const throw(null_dereference);

The set_value method allows the contents of a smart pointer to be changed:

s1.set_value("Hello World");

There is also an explicit form of the -> operator:

T* smart_ptr::pointer(void);
const T* smart_ptr::pointer(void) const;

Hint: The pointer() function does not throw an exception, so it is unlike the operator-> form in that way, If the smart pointer is null, then it returns a null simple pointer.

To show how these operators are used, consider the previous smart pointer to a sring examples:

*s1 += " 1, 2, 3";
s2->insert(s2->begin(), "testing, ");

Note that the +=, insert and begin functions all come from the string class.

This leaves both s1 and s2 pointing to a string containing the text "testing, testing 1, 2, 3".

Rewriting to use the functions rather than the operators:

s1.value() += " 1, 2, 3";
s2.pointer()->insert(s2.pointer()->begin(), "testing, ");

Forcing a Copy

It is sometimes desirable to detach two smart pointers to the same object by creating a new copy of the object and making one pointer point to the new object. This is achieved with the make_unique() member function. What this does is force the pointer being made unique to point to a unique copy of the object. If the pointer is already unique, it has no effect, but otherwise it creates a local copy unique to itself. For example, to make s1 and s2 in the earlier examples point to separate strings, do this:

s2.make_unique();

It is also possible to test whether two pointers point to the same object by using the aliases() function, which returns a bool and is usually used in a conditional:

if (s1.aliases(s2))
  s2.make_unique();

It is also possible to assign a unique copy by using the copy() method:

s1 = s2.copy();

The copy function returns a new smart pointer which contains a unique copy of the contents of the object - in this case s2. This smart pointer is then copied to s1 using the assignment operator - which makes the assignment efficiently by using alias counting methods. The end result is that s1 and s2 now point to different strings containing the same text.

There is an alternative form of the copy function:

s1.copy(s2);

This is functionally equivalent but saves an assignment - which is negligible anyway. However, some people prefer this style.

Clearing

There are two functions which clear the contents of a smart pointer, making it a null pointer:

void clear(void);
void clear_unique(void);

To make a smart pointer null - i.e. to force the object pointed to to be deleted right now, simply call the clear method:

s2.clear();

Warning: this deletes the object and makes the pointer null. It does not make the pointer unique first! Thus any other pointers pointing to the same object will become null too. You could call make_unique first, but that causes a copy to be made, only for the copy to be instantly deleted again when you call clear. The solution is to call clear_unique. This clears just this smart pointer by making it unique and null at the same time.

s2.clear_unique();

This makes s2 null and unique, leaving s1 still pointing to its original value.

Exceptions Thrown

The smart pointer classes can only throw one exception:

null_dereference
Thrown if a smart pointer is dereferenced when it is null. This is a general STLplus exception and is not specific to the smart pointer classes

See also the STLplus exceptions policy for a discussion of how exceptions are used in the library

Clone Variant

The discussion so far has concentrated on the variant called smart_ptr. This is designed to be used on simple types and classes where a copy can be made by simply calling the copy constructor for the contained object. However, when using hierarchies of derived classes (also known as polymorphic classes), this is not possible because copy constructors are not virtual, so you cannot make a copy this way. This is a well-known problem with C++'s implementation of polymorphism.

The solution to this problem is the cloning variant - a class called smart_ptr_clone. This variant of the smart pointer is designed for use with polymorphic types. It uses a solution to the non-virtual copy constructor problem which is suggested by Ellis and Stroustrup in "The Annoted LRM". The solution is to require the user of the smart pointer to provide a virtual clone method which makes a copy using new and the correct copy constructor, returning the result as a pointer to the base class. Each derivative overloads this function with its own variant which copies its own type. Thus the copy operation is now virtual and furthermore is localised to the individual derivative.

In order to use the smart_ptr_clone class, you must first derive the base class of your polymorphic set of classes from the clonable interface. Then, you must have a virtual clone method overloaded for all derivatives of that class.

The clonable interface is defined by the following class:

class clonable
{
public:
  virtual clonable* clone(void) const = 0;
};

As an example, consider the simple example of two classes, a base class and a derived class:

class base
{
  ...
};

class derived : public base
{
  ...
};

To make these classes suitable for use in a smart pointer, they must be made clonable. This is done by making the base class a derivative of the clonable interface and then adding the clone method to both classes:

class base : public clonable
{
  ...
  clonable* clone(void) const {return new base(*this);}
};

class derived : public base
{
  ...
  clonable* clone(void) const {return new derived(*this);}
};

You then create a smart pointer for this by using the base class as the template parameter:

typedef smart_ptr_clone<base> base_ptr;

The smart_ptr_clone class is identical to the smart_ptr class in its interface. The only difference is in its implementation. In every case where smart_ptr would copy the contained object, smart_ptr_clone clones the object instead.

Nocopy Variant

This variant of the smart pointer is designed for use on objects that cannot (or must not) be copied. An example would be when managing an object that contains, say, a file handle. It is essential that this not be copied because then you get the problem of deciding which copy is responsible for closing the file. To avoid the problem, wrap the file handle in a class and then manage a unique instance of it using a smart_ptr_nocopy. Indeed, this is exactly what I have done in the TextIO subsystem - a TextIO device contains a unique instance of a file buffer managed by a smart_ptr_nocopy object. This ensures that the file buffer cannot be copied and is closed when the last alias is destroyed.

The interface to the nocopy variant is a stripped-down version of smart_ptr with all operations that perform copying removed.

The following operations from smart_ptr have been removed because they use copying of the pointed-to object:

smart_ptr::smart_ptr(const T& data);
smart_ptr<T>& smart_ptr::operator=(const T& data);
void smart_ptr::set_value(const T& data);
void smart_ptr<T>::make_unique(void)
void smart_ptr<T>::copy(const smart_ptr<T>& data)
smart_ptr<T> smart_ptr<T>::copy(void) const

There are also no persistence functions provided for this class - it seems to me that if an object cannot be copied, it probably cannot be made persistent either.