Exceptional C++ - More auto_ptr

Home Blog Talks Books & Articles Training & Consulting

Prev
Up
Next

On the
blog
RSS feed November 4: Other Concurrency Sessions at PDC
November 3
: PDC'09: Tutorial & Panel
October 26: Hoare on Testing
October 23
: Deprecating export Considered for ISO C++0x

(See also the Exceptional C++ Errata List.)

The following new section should be inserted into Exceptional C++ on page 158 just before the subhead "The const auto_ptr Idiom."

auto_ptr and Exception Safety

Finally, auto_ptr is sometimes essential to writing exception-safe code. Consider the following function:

// Exception-safe?
//
String f()
{
    String result;
    result = "some value";
    cout << "some output";
    return result;
}

This function has two visible side effects: It emits some output, and it returns a String. A detailed examination of exception safety is beyond the scope of this Item, but the goal we want to achieve is the strong exception-safety guarantee, which boils down to ensuring that the function acts atomically -- even if there are exceptions, either all side effects will happen or none of them will.

Although the above code comes pretty close to achieving the strong exception-safety guarantee, there's still one minor quibble, as illustrated by the following calling code:

String theName;
theName = f();

The String copy constructor is invoked because the result is returned by value, and the copy assignment operator is invoked to copy the result into theName. If either copy fails, then f() has completed all of its work and all of its side effects (good), but the result has been irretrievably lost (oops).

Can we do better, and perhaps avoid the problem by avoiding the copy? For example, we could let the function take a non-const String reference parameter and place the return value in that:

// Better?
//
void f( String& result )
{
  cout << "some output";
  result = "some value";
}

This may look better, but it isn't, because the assignment to result might still fail which leaves us with one side effect complete and the other incomplete. Bottom line, this attempt doesn't really buy us much.

One way to solve the problem is to return a pointer to a dynamically allocated String, but the best solution is to go a step farther and return the pointer in an auto_ptr:

// Correct (finally!)
//
auto_ptr<String> f()
{
  auto_ptr<String> result = new String;
  *result = "some value";
  cout << "some output";
  return result;
      // rely on transfer of
      // ownership; this can't throw
}

This does the trick, since we have effectively hidden all of the work to construct the second side effect (the return value) while ensuring that it can be safely returned to the caller using only nonthrowing operations after the first side effect has completed (the printing of the message). We know that, once the cout is complete, the returned value will make it successfully into the hands of the caller, and be correctly cleaned up in all cases: If the caller accepts the returned value, the act of accepting a copy of the auto_ptr causes the caller to take ownership; and if the caller does not accept the returned value, say by ignoring the return value, the allocated String will be automatically cleaned up as the temporary auto_ptr holding it is destroyed. The price for this extra safety? As often happens when implementing strong exception safety, the strong safety comes at the (usually minor) cost of some efficiency -- here, the extra dynamic memory allocation. But, when it comes to trading off efficiency for correctness, we usually ought to prefer the latter!

Make a habit of using smart pointers like auto_ptr in your daily work. auto_ptr neatly solves common problems and will make your code safer and more robust, especially when it comes to preventing resource leaks and ensuring strong exception safety. Because it's standard, it's portable across libraries and platforms, and so it will be right there with you wherever you take your code.

Copyright 2009 Herb Sutter