- Overview
-
Table of Contents
- Special Member Functions: Constructors, Destructors, and the Assignment Operator
- Operator Overloading
- Memory Management
- Templates
- Namespaces
- Time and Date Library
- Streams
- Object-Oriented Programming and Design Principles
- The Standard Template Library (STL) and Generic Programming
- Exception Handling
- Runtime Type Information (RTTI)
- Signal Processing
- Creating Persistent Objects
- Bit Fields
- New Cast Operators
- Environment Variables
- Variadic Functions
- Pointers to Functions
- Function Objects
- Pointers to Members
- Lock Files
- Design Patterns
- Dynamic Linking
- Tips and Techniques
- Five Things You Need to Know About C++11 Unions
- A Tour of C99
- A Tour of C1X
-
C++0X: The New Face of Standard C++
- Reference Wrapper
- The Performance Technical Report
- auto for the People
- Ironing Templates' Syntactic Wrinkles
- Visual C++ Becomes ISO Compliant
- A Garbage Collector for C++
- C99 Core Features in C++0X
- The <code>shared_ptr</code> Class
- The shared_ptr Class, II
- Lambda Expressions and Closures, Part I
- Lambda Expressions and Closures, Part II
- Lambda Expressions and Closures, Part III
- The Type Traits Library, Part I
- The Type Traits Library, Part II
- The Type Traits Library, Part III
- finally Revisited
- The Any Library
- The nullptr Keyword Proposal
- Delegating Constructors
- The Explicit Conversion Operators Proposal
- Conditionally-Supported Behavior
- The weak ptr Class Template, Part I
- The weak ptr Class Template, Part II
- POD Types Revisited
- The rvalue Reference Proposal, Part I
- The rvalue Reference Proposal, Part II
- Proposal for New String Algorithms
- Concepts, Part I
- Concepts, Part II
- constexpr: Generalized Constant Expressions
- The <u>constexpr</u> Proposal: Constructors
- Strongly-Typed enum Types
- C++09: The Road Ahead
- C++09: Proposals by Statuses
- Changing Undefined Behavior to Diagnosable Errors
- New Character Types
- The __func__ Predeclared Identifier is Coming to C++
- Static Assertions
- The extern template Proposal
- Variadic Templates, Part I
- Variadic Templates, Part II
- Variadic Templates, Part III -- Critique
- Using unique_ptr, Part I
- Using unique_ptr, Part II
- Unrestricted Unions, Part I
- Unrestricted Unions, Part II
- Unrestricted Unions, Part III
- Types With No Linkage as Template Arguments
- New Initialization Syntax
- Initializer Lists and Sequence Constructors
- New Standard Library Algorithms
- Class Member Initializers
- Inheriting Constructors
- Introducing Attributes
- The Removal of Concepts From C++0x
- The Future of C++0x, Part I
- The Future of C++0X, Part II
- The Debate About Attributes, Part I
- The Debate About Attributes, Part II
- The Debate About Attributes, Part III
- The Debate About Attributes, Part IV
- Forward Declarations of Enum Types
- The SCARY Iterators Proposal, Part I
- The SCARY Iterators Proposal, Part II
- Heading for Deprecation: <tt>export</tt>, Exception Specification and <tt>register</tt>
- The Rejection of the Unified Function Syntax Proposal
- Rvalue References as Object Members
- FCD Approved
- The Debate on noexcept, Part I
- The Debate on noexcept, Part II
- The Debate on noexcept, Part III
- About-face -- [[Attributes]] to Be Replaced with Keywords
- Will Delegating Constructors Be Removed From C++0x?
- Rvalue References: Past, Present and Future, Part I
- Rvalue References: Past, Present and Future, Part II
- Rvalue References: Past, Present and Future, Part III
- A Move in the Right Direction, Part I
- A Move in the Right Direction, Part II
- New Keywords for Inheritance Control, Part I
- New Keywords for Inheritance Control, Part II
- FDIS Approved
- C++0x Concurrency
- The Reflecting Circle
- We Have Mail
- The Soapbox
- Numeric Types and Arithmetic
- Careers
- Locales and Internationalization
Using unique_ptr, Part I
Last updated Jan 1, 2003.
The turbulent history of auto_ptr eventually led to its deprecation in the C++0x Working Paper (I'm grateful to Howard Hinnant for pointing it out to me). C++0x introduces a new smart pointer class called unique_ptr which supports move semantics with at least the same efficiency of auto_ptr, but without the notorious limitations and runtime surprises that are associated with the latter. Find out more about the new unique_ptr class template and its usage here.
Why auto_ptr Had To Be Deprecated
The repeated modifications and patches of auto_ptr improved its safety to some extent but couldn't eliminate one serious flaw in its design: Calling a generic algorithm (e.g., std::sort()) that will operate on auto_ptr is risky because the generic algorithm might assume that something that looks like a copy operation -- is truly a copy operation. I'll show an example:
vector<auto_ptr<int> > vi; //..populate vi sort(vi.begin(), vi.end(), indirect_less());
Depending on the implementation of sort(), the above line of code might cause undefined behavior (often leading to a crash). The problem is that some implementations of sort() will pick an element out of the sequence, and store a local copy of it. Such implementations are perfectly valid and standard compliant. However, look what happens when they encounter vi:
value_type pivot = *mid_point; //seemingly a copy operation, but no quite so!
The sort() algorithm assumes that pivot and *mid_point are equivalent. However when value_type is an auto_ptr, this assumption fails because what appears to be a copy operation is in fact a move operation. Consequently, the algorithm fails. To fix this bug, auto_ptr was patched several years ago, disallowing standard containers to copy from const auto_ptr. This fix provides only a partial solution though. Users can still store auto_ptr objects in a built-in array. When you apply sort() to such an array, the results can be undefined behavior -- or perfectly valid, depending on the underlying implementation of sort().
Generally speaking, it's a bad idea to move from lvalues using copy syntax. Other syntax for moving should be used instead or else generic code is likely to initiate a move when a copy was intended, thus causing undefined behavior. Because auto_ptr moves from lvalues using copy syntax, it's fundamentally unsafe.
Deprecating auto_ptr
After years of debates, the C++ standards committee decided that auto_ptr should be deprecated. Since the deprecation alone will not make the problems of auto_ptr disappear it was decided that a new smart pointer class with more or less the same strict ownership semantics of auto_ptr -- but with a cleaner and safer interface -- should be added to C++0x. That replacement class is called unique_ptr. If you have used auto_ptr before, you will find unique_ptr's usage pretty straightforward. However, unlike auto_ptr, unique_ptr requires an explicit move operation for moving from lvalues; you can't use copy syntax for that purpose. Consequently, STL containers and algorithms, and in fact, any generic code will either fail to compile when operating on unique_ptr (instead of leaving the bug unreported and crashing at runtime), or it will produce valid results if unique_ptr is allowed. In addition to solving the safety problems of auto_ptr, the designers of unique_ptr decided to extend its functionality without adding space or runtime overhead (unless such overhead is explicitly requested) while still maintaining source compatibility with auto_ptr (except the ability to move from an lvalue with copy syntax).
Anything auto_ptr Can Do, But Better
It should be noted that unique_ptr is not a 100% source-compatible drop-in replacement for auto_ptr. If it were, the standards committee could simply patch auto_ptr instead of deprecating it and introducing a new class template as a replacement. That said everything you can do with auto_ptr, unique_ptr will do as well with the same syntax except for moving from lvalues:
#include <utility> using std::auto_ptr; using std::unique_ptr; // default construction auto_ptr<int> ap; unique_ptr<int> up; // initialize with pointer auto_ptr<int> ap(new int(9)); unique_ptr<int> up(new int(9)); // dereference *ap = 23; *up = 23; // reset ap.reset(); up.reset(); assert (sizeof(auto_ptr<T>) == sizeof(unique_ptr<T>));
When it comes to copy operations, there's a difference:
auto_ptr<int> ap1(new int); auto_ptr<int> ap2 = ap1; // OK, albeit unsafe unique_ptr<int> up1(new int); unique_ptr<int> up2 = up1; // compilation error: private copy ctor inaccessible
If you really want to transfer ownership from the lvalue unique_ptr, you have to use an explicit std::move() call:
unique_ptr<int> up1(new int); unique_ptr<int> up2 = move(up1); // ok, explicit move
unique_ptr permits move from rvalues with copy syntax using a move constructor (which binds to rvalues), while blocking the copy from lvalues by making the copy constructor (which binds to lvalues) private:
template <class T>
class unique_ptr
{
public:
unique_ptr(unique_ptr&& u); // rvalues bind here
private:
unique_ptr(const unique_ptr&); // lvalues bind here
};
In the next part of this series I will discuss passing unique_ptr to a function and returning unique_ptr from a function, using unique_ptr with algorithms and containers, customizing a deleter, and using unique_ptr for storing arrays.
