[This is a C++11-updated version of the original GotW #56.]

JG Questions

1. In each of the following statements, what can you say about the order of evaluation of the functions f, g, and h and the expressions expr1 and expr2? Assume that expr1 and expr2 do not contain more function calls.

// Example 1(a) // f( expr1, expr2 ); // Example 1(b) // f( g( expr1 ), h( expr2 ) );

Guru Questions

2. In your travels through the dusty corners of your company’s code archives, you find the following code fragment:

// Example 2 // In some header file: void f( T1*, T2* ); // At some call site: f( new T1, new T2 );

Does this code have any potential exception safety or other problems? Explain.

3. As you continue to root through the archives, you see that someone must not have liked Example 2 because later versions of the files in question were changed as follows:

// Example 3 // In some header file: void f( std::unique_ptr<T1>, std::unique_ptr<T2> ); // At some call site: f( std::unique_ptr<T1>{ new T1 }, std::unique_ptr<T2>{ new T2 } );

What improvements does this version offer over Example 2, if any? Do any exception safety problems remain? Explain.

4. Demonstrate how to write a make_unique facility that solves the safety problems in Example 3 and can be invoked as follows:

// Example 4 // In some header file: void f( std::unique_ptr<T1>, std::unique_ptr<T2> ); // At some call site: f( make_unique<T1>(), make_unique<T2>() );

Solution

1. In each of the following statements, what can you say about the order of evaluation of the functions f, g, and h and the expressions expr1 and expr2? Assume that expr1 and expr2 do not contain more function calls.

The answer hinges on the following basic rules: All of a function’s arguments must be fully evaluated before the function is called. This includes the completion of any side effects of expressions used as function arguments.

Once the execution of a function begins, no expressions from the calling function may begin or continue to be evaluated until execution of the called function has completed. Function executions never interleave with each other.

Expressions used as function arguments may generally be evaluated in any order, including interleaved, except as otherwise restricted by the other rules. In ISO C++11, these intra-thread ordering constraints are specified by the “sequenced before” relation, which constrains the transformations that compilers and hardware can perform within a single thread of execution. This replaces, but is intended to be identical to, the older C/C++ formulation of sequence points. Given those rules, let’s see what happens in our opening examples:

// Example 1(a) // f( expr1, expr2 );

In Example 1(a), all we can say is that both expr1 and expr2 must be evaluated before f is called.

That’s it. The compiler may choose to perform the evaluation of expr1 before, after, or interleaved with the evaluation of expr2. There are enough people who find this surprising that it comes up as a regular question in C++ forums, but it’s just a direct result of the C and C++ rules about intra-thread sequencing.

// Example 1(b) // f( g( expr1 ), h( expr2 ) );

In Example 1(b), the functions and expressions may be evaluated in any order that respects the following rules:

expr1 must be evaluated before g is called.

expr2 must be evaluated before h is called.

Both g and h must complete before f is called.

The evaluations of expr1 and expr2 may be interleaved with each other, but nothing may be interleaved with any of the function calls. For example, no part of the evaluation of expr2 or any of the execution of h may occur from the time g begins until it ends; however, it’s possible for h to be called either before or after g.

Some Function Call Exception Safety Problems

2. In your travels through the dusty corners of your company’s code archives, you find the following code fragment:

// Example 2 // In some header file: void f( T1*, T2* ); // At some call site: f( new T1, new T2 );

Does this code have any potential exception safety or other problems? Explain.

Yes, there are several potential exception safety problems.

Brief recap: An expression such as new T1 is called, simply enough, a new-expression. Recall what a new-expression really does (ignoring in-place and array forms for simplicity, because they’re not very relevant here):

it allocates memory;

it constructs a new object in that memory; and

if the construction fails because of an exception the allocated memory is freed.

So each new-expression is essentially a series of two function calls: one call to operator new (either the global one, or one provided by the type of the object being created), and then a call to the constructor.

For Example 2, consider what happens if the compiler decides to generate code that performs the following steps in order:

allocate memory for the T1 construct the T1 allocate memory for the T2 construct the T2 call f

The problem is this: If either step 3 or step 4 fails because of an exception, the C++ standard does not require that the T1 object be destroyed and its memory deallocated. This is a classic memory leak, and clearly Not a Good Thing.

Another possible sequence of events is the following:

allocate memory for the T1 allocate memory for the T2 construct the T1 construct the T2 call f

This sequence has, not one, but two exception safety problems with different effects:

If step 3 fails because of an exception, then the memory allocated for the T1 object is automatically deallocated (step 1 is undone), but the standard does not require that the memory allocated for the T2 object be deallocated. The memory is leaked.

If step 4 fails because of an exception, then the T1 object has been allocated and fully constructed, but the standard does not require that it be destroyed and its memory deallocated. The T1 object is leaked.

“Hmm,” you might wonder, “then why does this exception safety loophole exist at all? Why doesn’t the standard just prevent the problem by requiring compilers to Do the Right Thing when it comes to cleanup?”

Following the spirit of C in the matter of efficiency, the C++ standard allows the compiler some latitude with the order of evaluation of expressions, because this allows the compiler to perform optimizations that might not otherwise be possible. To permit this, the expression evaluation rules are specified in a way that is not exception-safe, so if you want to write exception-safe code you need to know about, and avoid, these cases.

Fortunately, you can do just that and prevent this problem. Perhaps a smart pointer like unique_ptr could help?

3. As you continue to root through the archives, you see that someone must not have liked Example 2 because later versions of the files in question were changed as follows:

// Example 3 // In some header file: void f( std::unique_ptr<T1>, std::unique_ptr<T2> ); // At some call site: f( std::unique_ptr<T1>{ new T1 }, std::unique_ptr<T2>{ new T2 } );

What improvements does this version offer over Example 2, if any? Do any exception safety problems remain? Explain.

This code attempts to “throw unique_ptr at the problem.” Many people believe that a smart pointer is an exception-safety panacea, a touchstone or amulet that by its mere presence somewhere nearby can help ward off compiler indigestion.

It is not. Nothing has changed. Example 3 is still not exception-safe, for exactly the same reasons as before.

Specifically, the problem is that the resources are safe only if they really make it into a managing unique_ptr, but the same problems already noted can still occur before either unique_ptr constructor is ever reached. This is because both of the two problematic execution orders mentioned earlier are still possible, but now with the unique_ptr constructors tacked onto the end before invoking f. For one example:

allocate memory for the T1 construct the T1 allocate memory for the T2 construct the T2 construct the unique_ptr<T1> construct the unique_ptr<T2> call f

In the above case, the same problems are still present if either of steps 3 or 4 throws. Similarly with:

allocate memory for the T1 allocate memory for the T2 construct the T1 construct the T2 construct the unique_ptr<T1> construct the unique_ptr<T2> call f

Again, the same problems are present if either of steps 3 or 4 throws.

Fortunately, though, this is not a problem with unique_ptr; it’s just being used the wrong way, that’s all. Let’s see how to use it better.

Enter make_unique

4. Demonstrate how to write a make_unique facility that solves the safety problems in Example 3 and can be invoked as follows:

// Example 4 // In some header file: void f( std::unique_ptr<T1>, std::unique_ptr<T2> ); // At some call site: f( make_unique<T1>(), make_unique<T2>() );

The basic idea is:

We want to leverage the fact that functions called from the same thread won’t interleave, so we want to provide a function that does the work of allocation and construction of the object and construction of the unique_ptr.

Because the function should be able to work with any type, we want to express it as a function template.

Because the caller will want to pass constructor parameters from outside make_unique, we’ll use the C++11 perfect forwarding style to pass those along to the new-expression inside make_unique.

Because shared_ptr already has an analogous std::make_shared, for consistency we’ll call this one make_unique. (That C++11 doesn’t include make_unique is partly an oversight, and it will almost certainly be added in the future. In the meantime, use the one provided below.)

Putting the ingredients together, we get:

template<typename T, typename ...Args> std::unique_ptr<T> make_unique( Args&& ...args ) { return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) ); }

This solves the exception safety problems. No sequence of generated code can cause resources to be leaked, because now all we have is two functions, and we know that one must be executed entirely before the other. Consider the following evaluation order:

call make_unique<T1> call make_unique<T2> call f

If step 1 throws, there are no leaks because make_unique is itself strongly exception-safe.

If step 2 throws, then is the temporary unique_ptr<T1> created by step 1 guaranteed to be cleaned up? Yes, it is. One might wonder: Isn’t this pretty much the same as the new T1 object created in the corresponding case in Example 2, which isn’t correctly cleaned up? No, this time it’s not quite the same, because here the unique_ptr<T1> is actually a temporary object, and cleanup of temporary objects is correctly specified in the standard. From the standard, in 12.2/3 (unchanged from C++98):

Temporary objects are destroyed as the last step in evaluating the full-expression that (lexically) contains the point where they were created. This is true even if that evaluation ends in throwing an exception.

Guidelines:

Prefer to allocate objects to be managed by a shared_ptr using make_shared, and objects to be managed by a unique_ptr with make_unique.

Although Standard C++ does not yet have make_unique, this is mostly an oversight and it will almost certainly eventually be added. In the meantime, use the version shown above, and your code will be forward-compatible with the likely direction of the C++ standard.

Avoid using plain new or other raw unmanaged allocation directly. Instead, use a factory like make_unique that wraps the raw allocation and immediately passes it to another object that will own the resource. Often that owning object will be a smart pointer, but it can be a scope guard or any other kind of owning object whose destructor will deallocate the resource safely.

Acknowledgments

This Item was prompted by a discussion thread on comp.lang.c++.moderated. This solution draws on observations presented by James Kanze, Steve Clamage, and Dave Abrahams in that and other threads, and in private correspondence. Thanks also to Clark Nelson, who drafted C++11’s “sequenced before” wording, for clarifying the C++11 status quo.