Scrupulous Abstractions

Spare time experimentation with C++11

C++11 Style - Never write “new” or “delete” again - And the make_unique implementation of S.T.Lavavej

One of the most striking changes with the modern C++ style that comes with the new standard from last year, C++11, is the idea that we should not write new or delete again!

In the past, the most important reason for writing “new” was to create an object that could be returned from a function or method without the overhead of copying the new object. The reason this is no longer required is called move-semantics. Among other things, move semantics mean that when you return an std::vector<std::string> by value, the contents are not copied, but transferred, moved. For the details on how that is possible, I give a few starting points a the end of this post.

Now, what should we do instead of newing up raw pointers? Here is an example:

Instead of writing

std::vector<double>* makeVector(){   
std::vector<double>* newVec = new std::vector<double>();
for(int i=0;i<30;++i){
newWec->push_back(i);
}
return newVec;
}

we should simply write:

std::vector<double> makeVector(){   
std::vector<double> newVec();
for(int i=0;i<30;++i){
newWec.push_back(i);
}
return newVec;
}

This has a number of benefits. It’s cleaner, it’s as fast as using raw pointers, and it’s automatically exception safe, since it’s using the RAII idiom. RAII means that all resources are held by objects with destructors, so that no resource (in this case the array and its contents) can be lost/leaked/forgotten. 

In this case it means that the destructor of the vector will be called as soon as the vector is not needed anymore. Raw pointers do not have destructors which deallocate the held object, so we would have to make sure that someone, somewhere will always call delete (once) on the pointer, and that the object is never referenced after that. That has turned out to be very hard.

Return by smart pointers - unique_ptr and shared_ptr

The absolute majority of classes in the C++11 standard library are move-aware, so there is no reason to return values by raw pointer any more. There are a few exceptions - Classes that do not have move-constructors, and which objects are impossible or expensive to copy. An example is the new container std::array. If we follow the rule to never use new or delete, a function returning an array could look like this:

using LargeArray= array<int,10000>; // This is the new type of typedef: Defines a type: 10000 ints

LargeArray giveMeMyArray(){
LargeArray myObj;
myObj.at(3)=5;
return myObj;
}

this is exception safe and clean, but very inefficient since the array is copied out (in this simple example this may not be true, due to return-value-optimization, but that’s not the point). Instead, we reach for a unique_ptr, a smart pointer that behaves similarly to an old-style pointer, but with two important exceptions: 1. It has a destructor that deletes the pointed to object at destruction. 2. It is moveable but not copyable.

This is the most direct way to rewrite our function using unique_ptr:

unique_ptr<LargeArray> giveMeMyArray(){
unique_ptr<LargeArray> myObj(new LargeArray());
myObj->at(3)=5;
return myObj;
}

This is fine, but it breaks our rule of thumb: never use new. To get around that it is agreed best-practice to use a generic helper function called make_unique.

Using make_unique, the code looks like this:

unique_ptr<LargeArray> giveMeMyArray(){
auto myObj = make_unique<LargeArray>();
myObj->at(3)=5;
return myObj;
}

This follows the same pattern as for the other smart pointer in the C++ standard library: shared_ptr, with the helper functions make_shared and allocate_shared.

Using make_unique is not only more clean (in that the advice about new becomes more solid), but is actually also required for exception safety in pretty normal use cases (Se the list of references at the end of this post).

The make_unique implementation of Stephan T. Lavavej

Unfortunately, make_unique did not get into the C++11 standard, so we have to provide it ourselves. The following implementation of make_unique has been suggested by C++ standard-libraries developer Stephan T. Lavavej. For his own explanation, look at his c9Video: Core C++ 6/n, starting at the 48 min mark. Here’s the code:

/* stephantl_make_unique.h
* Based on code by Stephan T. Lavavej at http://channel9.msdn.com/Series/
* C9-Lectures-Stephan-T-Lavavej-Core-C-/STLCCSeries6
*/

#ifndef STEPHANTL_MAKE_UNIQUE_H_
#define STEPHANTL_MAKE_UNIQUE_H_

#include <memory>
#include <utility>
#include <type_traits>

namespace fut_stl {
namespace impl_fut_stl {
template<typename T, typename ... Args>
std::unique_ptr<T> make_unique_helper(std::false_type, Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

template<typename T, typename ... Args>
std::unique_ptr<T> make_unique_helper(std::true_type, Args&&... args) {
static_assert(std::extent<T>::value == 0,
"make_unique<T[N]>() is forbidden, please use make_unique<T[]>(),");
typedef typename std::remove_extent<T>::type U;
return std::unique_ptr<T>(new U[sizeof...(Args)]{std::forward<Args>(args)...});
}
}

template<typename T, typename ... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
return impl_fut_stl::make_unique_helper<T>(
std::is_array<T>(),std::forward<Args>(args)... );
}
}
#endif

This code contains a single template function make_unique and two helper classes. My only modification from the code found in STL’s video is that I placed make_unique in a namespace: fut_stl, and within that namespace, I placed the helper classes into a helper namespace. I find this to be clearer, and it also helps auto-completion (for example in Eclipse and Visual Studio) to not have implementations details be named make_*. Here are the three examples, directly from the same video:

#include "stlav_make_unique.h"
#include <iostream>
#include <ostream>
#include <string>

int main(){
using namespace fut_stl;
using namespace std;
auto a = make_unique<int>(1729);
cout << *a << endl;

auto s = make_unique<string>(5, 'x');
cout << *s << endl;

auto b = make_unique<int[]>(11, 22, 33);
cout << b[0] << " "<< b[1] << " "<< b[2] << endl;
}

This is extremely useful, and you don’t have to understand why it works. The advantage of this version of make_unique over other implementations is that it works with arrays (here I mean int[] for example) while also preventing improper use. If you are interested I strongly recommend following the STL Video series I link to below. Even as a C++ beginner, you should however learn how unique_ptr (as well as shared_ptr) works.

References and more

Any level:

Introductory -

Andrzej’s C++ blog

Intermediate - “Rule of zero”, about the use of unique_ptr for class members:

Above Intermediate - Stephan T. Lavavej. Videos:

Above Intermediate - Herb Sutter: (about exception safety of make_unique and more)

Standard library references:

.

comments powered by Disqus