Last time [1], I gave a survey of the first batch of suggested library extensions that were considered at the October 2001 C++ standards meeting in Redmond, Washington, USA. This time, as promised, we'll take a closer look at one of the proposed facilities  smart pointers, which were discussed again at the following April 2002 standards meeting in Curaçao, in the Netherlands Antilles.

And Then There Was One

In today's Standard C++, there's only one smart pointer: std::auto_ptr .

Considering that it's such a tiny feature, auto_ptr sure has received copious attention. The main reason for the attention is that attention is deserved, both positively and negatively. On the one hand, auto_ptr is very useful for specific situations and those uses deserve to be described; see the discussion in Items 19 and 37 of Exceptional C++ [2], Item 10 of More Effective C++ [3], and my article "Using auto_ptr Effectively," available online [4]. On the other hand, auto_ptr can also be moderately suspicious to highly dangerous in other situations, such as the minefield of trying to put auto_ptr s into standard containers like vector and map ; see for example Item 21 of More Exceptional C++ [5] and Item 28 of More Effective C++ [3]. (On the gripping hand, as Niven and Pournelle might say, auto_ptr relies on some fairly obscure language trickery to make it deliberately not work in some dangerous cases. Fortunately this trickery gets less press  the details are not for the faint of heart. Suffice it to say that auto_ptr is designed with some sleight of hand that's intended to make it deliberately break when used with, say, standard containers.)

So today's Standard C++ has exactly one smart pointer: auto_ptr . That's it. So it must be all you need, right? Not a bit of it. There's more to this story.

It's actually a real shame that auto_ptr is the only standard smart pointer, for at least two reasons. First, auto_ptr doesn't do all the useful things that smart pointers might be good for; there are many good uses of smart pointers that poor auto_ptr was never designed to support, and shouldn't, and doesn't. Second, auto_ptr can be a dangerous snake that turns and bites you on the hand if you do try to use it in some of those other ways.

The first piece of good news is that many good and useful smart pointers are available and were available even before auto_ptr . The only problem is that they weren't, and aren't, standard. That's somewhere between disappointing and annoying, depending on how portable your code needs to be  standard features are of course generally the most portable.

The second piece of good news is that those alternative smart pointers are now strong candidates for inclusion into the Standard. (Some could have been in the original Standard; see [1]. But better late than never.)

If One Is Good, Six Are Better... (?)

The Boost library ships with five additional smart pointers [6]. All of these smart pointer templates take a single type parameter specifying the type of the object to be held. Briefly, here they are:

scoped_ptr : a non-copyable smart pointer intended to be used as an auto (stack) object. When a scoped_ptr goes out of scope and is destroyed, it will automatically delete the single object it points to. Arguably, scoped_ptr is what auto_ptr ought to have been in the first place, way back when auto_ptr was first meant to be really an "auto" pointer. But scoped_ptr doesn't support the modern auto_ptr 's important additional usefulness for sources and sinks (described in [4]).

: a non-copyable smart pointer intended to be used as an auto (stack) object. When a goes out of scope and is destroyed, it will automatically delete the single object it points to. Arguably, is what ought to have been in the first place, way back when was first meant to be really an "auto" pointer. But doesn't support the modern 's important additional usefulness for sources and sinks (described in [4]). scoped_array : like scoped_ptr , but owns an array instead of a single object.

: like , but owns an array instead of a single object. shared_ptr : a reference-counted smart pointer intended to be used to share "handles" to the pointed-at object. When a shared_ptr goes out of scope and is destroyed, if it is the last shared_ptr pointing at the owned object, it will automatically delete the owned object  a classic case of "last one out, turn off the lights." Note that shared_ptr does support auto_ptr 's important use for sources and sinks. More importantly, it can be safely used in a container, which for auto_ptr s is not allowed and highly dangerous.

: a reference-counted smart pointer intended to be used to share "handles" to the pointed-at object. When a goes out of scope and is destroyed, if it is the last pointing at the owned object, it will automatically delete the owned object  a classic case of "last one out, turn off the lights." Note that does support 's important use for sources and sinks. More importantly, it can be safely used in a container, which for s is not allowed and highly dangerous. shared_array : like shared_ptr , but owns an array instead of a single object.

: like , but owns an array instead of a single object. weak_ptr : to be used in conjunction with a shared_ptr . If you have an object that's already managed by one or more shared_ptr s, you can create weak_ptr s to it too. Now, the shared_ptr s still follow the "last one out, turn off the lights" policy; the last shared_ptr will delete the owned object even if there are still weak_ptr s to it. How is weak_ptr then any better than just a bald pointer to the object? Because the shared_ptr that turns off the lights will also check to see if any weak_ptr s still point to that object and set them to null if they do. That's a level of safety you don't get with plain old raw pointers.

Let me repeat some advice from last time, because it's worth repeating: if you know nothing else about Boost, know about shared_ptr . It's especially valuable if you ever want to have a container of pointers, because what you almost always really want is a container of shared_ptr s. For one thing, they'll be managed for you and will get cleaned up correctly, and they'll avoid the usual pitfalls of using mutating STL algorithms on containers of bald pointers. For another, they are not auto_ptr s, which is a Good Thing because auto_ptr s should never be put into STL containers.

The Boost approach is to have lots of special-purpose smart pointer types, one for each kind of smart pointer behavior. In this model, a smart pointer template is quite simple:

template<typename T> class scoped_ptr; template<typename T> class scoped_array; template<typename T> class shared_ptr; template<typename T> class shared_array; template<typename T> class weak_ptr;

Only one template parameter is needed, to specify the type of the owned object. It is both an advantage and a disadvantage that these little class templates can all have very different interfaces if they want to. On the one hand, it allows customization, say when a member function might make sense for one but not another smart pointer. But, on the other hand, it also means that it's easy to get inconsistent interfaces, especially when people start extending the facility by providing more smart pointers of their own, both within and outside the Boost library.

If Six Are Good, Infinity Is Better... (?!)

Although Boost chooses to follow the model of designing special-purpose smart pointer templates, we can ask: "Why stop there?" In fact, some very bright language designers, notably Andrei Alexandrescu in Modern C++ Design [7], have proposed that a "one size fits all" über-pointer may be an achievable dream, using policy-based design.

Policy-based design takes the view that certain details, such as checking policies and ownership policies, can be abstracted out into their own classes, which are then supplied as template parameters to configure or customize the basic smart pointer. Thus Loki's SmartPtr template looks something like this:

template < typename T, template <class> class OwnershipPolicy = RefCounted, class ConversionPolicy = DisallowConversion, template <class> class CheckingPolicy = AssertCheck, template <class> class StoragePolicy = DefaultSPStorage > class SmartPtr;

Note that we still have the obligatory template parameter T that describes the type of the pointed-at object. But we also get policies that govern much of the behavior of the smart pointer. The policies may look cumbersome to type out, but they are defaulted for those who don't want or need to specify them; the default Loki::SmartPtr is a lot like a boost::shared_ptr . In fact, SmartPtr covers all that the four basic Boost smart pointers can do, and more. (An equivalent of Boost's weak_ptr could also be provided to work with a partial specialization of SmartPtr corresponding to shared_ptr .)

Indeed, Loki's SmartPtr is so much a superset of Boost's smart pointers that we would love to just create synonyms. In Standard C++, the following code won't work because it relies on a feature that was frankly just overlooked, namely typedef templates (for some existing discussion, see [8]; I'll be writing more about this up-and-coming feature in the future). Barring major surprises, such as the Earth suddenly deciding to break out of its usual orbit and head for Mars on a holiday, typedef templates will be in the next C++ Standard, C++0x (see [9]). If we had this feature, we could write the following synonym:

namespace boost { template<typename T> // typedef templates aren't standard typedef Loki::SmartPtr // yet, but let's overlook that for now... < T, // note, T still varies RefCounted, // but everything else is fixed DisallowConversion, NoCheck, DefaultSPStorage > shared_ptr; }

So that the usage would simply be, as usual:

shared_ptr<int> a; // simple -- no fuss, no muss!

Similar synonyms could be created for shared_array , scoped_ptr , scoped_array , and even std::auto_ptr . Such simple names for common configurations would make all the tedious template parameters go away for those common cases. We can't do that today, alas, but once typedef templates become commonly available we will be able to do this and more.

SmartPtr really then describes an unlimited family of smart pointers having a consistent interface. Note that because Loki's SmartPtr inherits publicly from its template parameter types, the interface does not have to be identical across the whole family; the policies could add some visible functionality too. Loki already comes with several predefined policies allowing hundreds of combinations. But you can add your own policies, such as for managing COM and CORBA distributed objects and other advances uses, so the size of the SmartPtr family really is effectively unlimited.

Summary

The standards committee is still considering these options and welcomes other smart pointer submissions. In particular, there is ongoing discussion, with different points of view, about whether the "one size fits all" customizable policy-based design is the way to go, or whether just a few special-purpose smart pointer templates are needed. Want to weigh in? Start discussion on the Boost reflector [10] or on the comp.std.c++ newsgroup.

Next time: more news from the April 2002 standards meeting in Curaçao. Stay tuned.

References

[1] H. Sutter. "The New C++: The Group of Seven  Extensions under Consideration for the C++ Standard Library," C/C++ Users Journal C++ Experts Forum, April 2002, <www.cuj.com/experts/2004/sutter.htm>.

[2] H. Sutter. Exceptional C++ (Addison-Wesley, 2000).

[3] S. Meyers. More Effective C++ (Addison-Wesley, 1996).

[4] H. Sutter. "Using auto_ptr Effectively," C/C++ Users Journal, October 1999, available online at <www.gotw.ca/publications/using_auto_ptr_effectively.htm>.

[5] H. Sutter. More Exceptional C++ (Addison-Wesley, 2002).

[6] <www.boost.org/libs/smart_ptr/index.htm>

[7] A. Alexandrescu. Modern C++ Design (Addison-Wesley, 2001).

[8] H. Sutter. "Guru of the Week #79: Template Typedef," available online at <www.gotw.ca/gotw/079.htm>.

[9] H. Sutter. "The New C++," C/C++ Users Journal C++ Experts Forum, February 2002, <www.cuj.com/experts/2002/sutter.htm>.

[10] C++ Boost, <www.boost.org>.

About the Author

Herb Sutter (www.gotw.ca) is secretary of the ISO/ANSI C++ standards committee, author of the acclaimed books Exceptional C++ and More Exceptional C++, and one of the instructors of The C++ Seminar (www.gotw.ca/cpp_seminar). In addition to his independent writing and consulting, he is also C++ community liaison for Microsoft.