You are on page 1of 9

5/31/2017 Ten C++11 Features Every C++ Developer Should Use ­ CodeProject

12,958,457 members ﴾71,751 online﴿ Sign in

Search for articles, questions, tips
articles Q&A forums lounge

Ten C++11 Features Every C++ Developer Should Use
Marius Bancila, 2 Apr 2013 CPOL Rate this:
   4.95 ﴾151 votes﴿

This article discusses a series of features new to C++11 that all developers should learn and use.

This article discusses a series of features new to C++11 that all developers should learn and use. There are lots of new additions to the language and the standard
library, and this article barely scratches the surface. However, I believe some of these new features should become routine for all C++ developers. You could probably
find many similar articles evangelizing different C++11 features. This is my attempt to assemble a list of C++ features that should be a norm nowadays. 

Table of contents:  

Range‐based for loops
Override and final
Strongly‐typed enums
Smart pointers 
non‐member begin﴾﴿ and end﴾﴿
static_assert and type traits
Move semantics

Before C++11 the auto keyword was used for storage duration specification. In the new standard its purpose was changed towards type inference. auto is now a sort
of placeholder for a type, telling the compiler it has to deduce the actual type of a variable that is being declared from its initializer. It can be used when declaring
variables in different scopes such as namespaces, blocks or initialization statement of for loops.

Hide   Copy Code
auto i = 42;        // i is an int 
auto l = 42LL;      // l is an long long 
auto p = new foo(); // p is a foo*

Using auto usually means less code ﴾unless your type is int which is one letter shorter﴿. Think of iterators in STL that you always had to write while iterating over
containers. It makes obsolete creating typedefs just for the sake of simplicity.

Hide   Copy Code
std::map<std::string, std::vector<int>> map; 
for(auto it = begin(map); it != end(map); ++it)  


You should note that auto cannot be used as the return type of a function. However, you can use auto in place of the return type of function, but in this case the
function must have a trailing return type. In this case auto does not tell the compiler it has to infer the type, it only instructs it to look for the return type at the end of
the function. In the example below the return type of function compose is the return type of operator+ that sums values of types T1 and T2.

Hide   Copy Code
template <typename T1, typename T2> 
auto compose(T1 t1, T2 t2) ‐> decltype(t1 + t2) 

   return t1+t2; 

auto v = compose(2, 3.14); // v's type is double­Cplusplus­Features­Every­Cplusplus­Developer 1/12

 to an integral type For backward compatibility 0 is still a valid null pointer value.  int i = nullptr.5/31/2017 Ten C++11 Features Every C++ Developer Should Use ­ CodeProject nullptr  Zero used to be the value of null pointers.    }  }  int arr[] = {1. Range‐based for loops C++11 augmented the for statement to support the "foreach" paradigm of iterating over collections. it is possible to iterate over C‐like arrays. using reinterpret_cast.  } Override and final I always founded the virtual methods badly designed in C++ because there wasn't ﴾and still isn't﴿ a mandatory mechanism to mark virtual methods as overridden in derived classes. This for each for is useful when you just want to get and do something with the elements of a collection/array and don't care about indexes. initializer lists and anything for which the non‐member begin() and end() functions are overloaded.  std::vector<int> v. However.     if(p1 == p2)  {  }  foo(nullptr).codeproject.push_back(1). there are subtle errors that can still arise. // error: A native nullptr can only be converted to bool or. std::vector<int>> map. Take for instance the following example: Hide   Copy Code class B   {  public:     virtual void f(short) {std::cout << "B::f" << std::endl.2.  v.  v.  class D : public B  {  public:  https://www.    for(auto v : kvp.5}.3. Hide   Copy Code void foo(int* p) {}  void bar(std::shared_ptr<int> p) {}  int* p1 = NULL.}  }. because you may have to look through the top of the hierarchy to check if the method is virtual.  map["one"] = v.  bool f = nullptr. In the new form.4. but also to bool ﴾as false﴿.  int* p2 = nullptr. The keyword nullptr denotes a value of type std::nullptr_t that represents the null pointer literal. The virtual keyword is optional and that makes reading code a bit harder.  for(const auto& kvp : map)   {    std::cout << kvp.  for(int& e : arr)   {    e = e*e. I have always­Cplusplus­Features­Every­Cplusplus­Developer 2/12 .  v. to make the code easier to read.second)    {       std::cout << v << std::endl. and that has drawbacks due to the implicit conversion to integral types. and encouraged people to use the virtual keyword on derived classes also.  bar(nullptr). But no implicit conversion to integral types exist.push_back(3). Implicit conversions exists from nullptr to null pointer value of any pointer type and any pointer‐to‐member types. iterators or number of elements.push_back(2). Hide   Copy Code std::map<std::string.first << std::endl.

and final.}  }. but the method in the base class is marked const.  Hide   Copy Code class B   {  public:     virtual void f(int) {std::cout << "B::f" << std::endl.  class D : public B  {  public:     virtual void f(int) override final {std::cout << "D::f" << std::endl. or any derived class.}  }. one takes an int.}  }. if using the override specifier﴿: 'D::f' : method with override specifier 'override' did not override any base class methods On the other hand if you intend to make a method impossible to override any more ﴾down the hierarchy﴿ mark it as final. Again. function declared as 'final' cannot be overridden by 'F::f' Strongly‐typed enums "Traditional" enums in C++ have some drawbacks: they export their enumerators in the surrounding scope ﴾which can lead to name collisions. The first example would become: Hide   Copy Code class B   {  public:     virtual void f(short) {std::cout << "B::f" << std::endl. to indicate that a method is supposed to be an override of a virtual method in a base class.}  }. the signature differ. called strongly‐typed enums. these two are overloads and not overrides. Hide   Copy Code class B   {  public:     virtual void f(int) const {std::cout << "B::f " << std::endl. These issues have been fixed in C++ 11 with the introduction of a new category of enums. so if you call f() through a pointer to B it will print B::f and not D::f. to indicate that a derived class shall not override a virtual method. This now triggers a compiler error ﴾the same error you'd get for the second example too. one takes a short.  class D : public B  {  public:     virtual void f(int) override {std::cout << "D::f" << std::endl.}  }. However.  class F : public D  {  public:     virtual void f(int) override {std::cout << "F::f" << std::endl. are no longer implicitly converted to integral types and can have a user‐specified underlying type ﴾a feature also added for traditional enums﴿.}  }.codeproject.  Here is another subtle error: the parameters are the same.  Fortunately there is now a way to describe your intentions.}  }. D::f is supposed to override B::f. They no longer export their enumerators in the surrounding scope. You may call f() through a pointer to B and expect to print D::f. If it's in a derived classes you can use both the override and final specifiers. That can be in the base class.}  }. They are specified with the enum class keywords. if two different enums in the same have scope define enumerators with the same name﴿. Two new special identifiers ﴾not keywords﴿ have been added: override. therefor B::f is just another method with the same name ﴾and overload﴿ and not an override.  class D : public B  {  public:     virtual void f(int) {std::cout << "D::f" << std::endl.5/31/2017 Ten C++11 Features Every C++ Developer Should Use ­ CodeProject    virtual void f(int) {std::cout << "D::f" << std::endl. while me method in the derived is not. they are implicitly converted to integral types and cannot have a user‐specified underlying­Cplusplus­Features­Every­Cplusplus­Developer 3/12 . Hide   Copy Code https://www. but it's printing B::f.

In the next example memory leaks could occur if seed﴾﴿ throws an error.codeproject.  if(p2)    foo(p2. make_shared<T> is a non‐member function and has the advantage of allocating memory for the shared object and the smart pointer with a single allocation. // transfer ownership  if(p1)    foo(p1. a cycle would be created and no object would ever be released﴿. Hide   Copy Code void foo(std::shared_ptr<int> p.  std::weak_ptr<int> wp = p.  std::shared_ptr<int> p2 = p1. therefore I just want to mention the smart pointers with reference counting and auto releasing of owned memory that are available: unique_ptr: should be used when ownership of a memory resource does not have to be shared ﴾it doesn't have a copy constructor﴿. but the children also must hold a reference to the parent. in order to access the object. shared_ptr: should be used when ownership of a memory resource should be shared ﴾hence the name﴿.       bar(p1). there can be situations where memory leaks can occur because of that. weak_ptr: holds a reference to an object managed by a shared_ptr.get()). In addition to possible overhead. The first declaration is equivalent to this one Hide   Copy Code auto p3 = std::make_shared<int>(42). If you want to transfer ownership of an object to another unique_ptr use std::move ﴾I'll discuss this function in the last paragraph﴿.     foo(p2. though the semantics are different since ownership is shared. Hide   Copy Code void foo(int* p)  {     std::cout << *p << std::endl.  std::unique_ptr<int> p2 = std::move(p1).  (*p2)­Cplusplus­Features­Every­Cplusplus­Developer 4/12 . Hide   Copy Code auto p = std::make_shared<int>(42). The third sample shows usage of weak_ptr. seed()).  https://www. After the ownership transfer. One. On the other hand the auto_ptr is obsolete and should no longer be used.5/31/2017 Ten C++11 Features Every C++ Developer Should Use ­ CodeProject enum class Options {None.  }  std::shared_ptr<int> p1(new int(42)). The second example shows shared_ptr. Smart pointers There have been tons of articles written on this subject.  }  std::unique_ptr<int> p1(new int(42)). the smart pointer that ceded the ownership becomes null and get() returns nullptr. int init)  {     *p = init. it is used to break dependency cycles ﴾think of a tree where the parent holds an owning reference ﴾shared_ptr﴿ to its children. Notice that you always must get a shared_ptr to the referred object by calling lock﴾﴿.  }  foo(std::shared_ptr<int>(new int(42)).get()). No such problem exists if using make_shared. Usage is similar. if this second reference was also an owning one. as opposed to the explicit construction of a shared_ptr via the contructor. but does not contribute to the reference count. All}. The first example below shows unique_ptr.get()). but it can be transferred to another unique_ptr ﴾move constructor exists﴿. When you should unique_ptr and when you should use shared_ptr depends on the ownership requirements and I recommend reading this discussion. Hide   Copy Code void foo(int* p)  {  }  void bar(std::shared_ptr<int> p)  {     ++(*p). that requires at least two allocations.  Options o = Options::All.

promoting uniformity. have been added to C++ and quickly rose to prominence. the code might have looked like this: Hide   Copy Code int arr[] = {1.  v. Overloads for C‐like arrays are also provided. &arr[0]+sizeof(arr)/sizeof(arr[0]).}).  if(pos != std::end(v))    std::cout << *pos << std::endl.}).}).codeproject.  if(wp. called lambda. std::end(v). If you attempt to write it using auto you get compilation error: Hide   Copy Code auto fib = [&fib](int n) {return n < 2 ? 1 : fib(n‐1) + fib(n‐2). Lambdas Anonymous functions. The key is to break this dependency cycle and explicitly specify the function's type using std::function. Imagine a lambda that represents a Fibonacci function.  https://www.reset(). [](int n) {std::cout << n << std::endl.  auto is_odd = [](int n) {return n%2==1.  auto end = &arr[0]+sizeof(arr)/sizeof(arr[0]). is_odd).push_back(3). so they can be extended to work with any type. std::end(arr).  std::for_each(std::begin(arr).  }  p. If the std::vector was instead a C‐like array. Let's take for instance the previous example where I was printing a vector and then looking for its first odd element.  v. You can use lambdas wherever a function object or a functor or a std::function is expected.3}. [](int n) {std::cout << n << std::endl. is_odd). If you try to lock on an expired weak_ptr ﴾the object is weakly reference has been released﴿ you get an empty shared_ptr. You can read about the syntax here. It is a powerful feature borrowed from functional programming. but more than that they are overloadable.    std::cout << *sp << std::endl.  if(pos != end)    std::cout << *pos << std::endl.}.expired())    std::cout << "expired" << std::endl.  std::for_each(&arr[0].}.push_back(2). This is a cyclic problem. With non‐member begin() and end() it could be put as this: Hide   Copy Code int arr[] = {1.  auto pos = std::find_if(std::begin(v).5/31/2017 Ten C++11 Features Every C++ Developer Should Use ­ CodeProject {    auto sp = wp. std::end(v). Hide   Copy Code std::vector<int> v. [](int n) {std::cout << n << std::endl.  std::for_each(std::begin(v). non‐member begin﴾﴿ and end﴾﴿ You probably noticed I have used in the samples above non‐member begin() and end() functions. yet the initializer contains a reference to it. therefore needs to know its type. consistency and enabling more generic programming.2.  auto is_odd = [](int n) {return n%2==1. that in turned enabled other features or powered libraries.  auto begin = &arr[0].2.  auto pos = std::find_if(begin. These are a new addition to the standard­Cplusplus­Features­Every­Cplusplus­Developer 5/12 .push_back(1).  v.lock().3}.}. They work with all STL containers. end.}. Hide   Copy Code error C3533: 'auto &': a parameter cannot have a type that contains 'auto'  error C3531: 'fib': a symbol whose type contains 'auto' must have an initializer  error C3536: 'fib': cannot be used before it is initialized  error C2064: term does not evaluate to a function taking 1 arguments The problem is auto means the type of the object is inferred from its initializer. A bit trickier are recursive lambdas. Hide   Copy Code std::function<int(int)> lfib = [&lfib](int n) {return n < 2 ? 1 : lfib(n‐1) + lfib(n‐2).

This is basically identical code to the std::vector version. Hide   Copy Code template <typename T1.  v.     auto pos = std::find_if(begin. [](int n) {std::cout << n << std::endl.  }  int arr[] = {1. "Size is too small").  v.push_back(2). If the assertion is false. and type transformation classes. static_assert and type traits static_assert performs an assertion check at compile‐time. is_odd). Iterator end)   {     std::for_each(begin. end. type traits classes. for creating compile‐time constants. std::end(arr)).     Vector<double. for getting new types by applying transformation on existing types.Size>' being compiled     with     [        T=double.  v. std::end(arr). the compiler displays the specified error message.     auto is_odd = [](int n) {return n%2==1.  foo(arr).5/31/2017 Ten C++11 Features Every C++ Developer Should Use ­ CodeProject auto is_odd = [](int n) {return n%2==1.  std::vector<int> v.  } Hide   Copy Code error C2338: Size is too small  see reference to class template instantiation 'Vector<T. typename T2>  auto add(T1 t1. There are several categories of classes in this header: helper classes.  foo(v).  if(pos != std::end(arr))    std::cout << *pos << std::endl. In the following example function add is supposed to work only with integral types. T2 t2) ‐> decltype(t1 + t2)  https://www. Hide   Shrink   Copy Code template <typename Iterator>  void bar(Iterator begin. to get type information at compile time.     if(pos != end)        std::cout << *pos << std::endl.  }. Hide   Copy Code template <typename T.     T _points[Size].codeproject.  int main()  {     Vector<int.}). is_odd). 16> a1. That means we can write a single generic method for all types supported by begin() and end().  }  template <typename C>  void foo(C c)  {     bar(std::begin(c). size_t N>  void foo(T(&arr)[N])  {     bar(std::begin(arr).2. These are a series of classes that provide information about types at compile time. size_t Size>  class Vector  {     static_assert(Size < 3. They are available in the <type_traits> header. end. nothing happens.3}.}.  auto pos = std::find_if(std::begin(arr).push_back(1).  }  template <typename T. std::end(c)).com/Articles/570638/Ten­Cplusplus­Features­Every­Cplusplus­Developer 6/12 .}.push_back(3).        Size=2     ] static_assert becomes more useful when used together with type traits.     return 0. If the assertion is true. 2> a2.

14) << std::endl. copying the values from the temporary. should not be necessary. That means an overhead of operations. meaning that you want to copy the objects pointers refer to. a destructor and a copy assignment operator. a container implementation ﴾such as a vector or a queue﴿ may have a pointer to an array of elements. The buffer is identified by a name ﴾just for the sake of showing a point revealed below﴿. and not the values of the pointers.T2)' being compiled     with     [        T2=double.e.        T1=int     ]  error C2338: Type T1 must be integral  see reference to function template instantiation 'T1 add<const char*. The move semantics allow modifying rvalues ﴾previously considered immutable and indistinguishable from const T& types﴿.int>(T1.T2)' being compiled     with     [        T1=const char *.codeproject. copying a sequence of elements. which is an rvalue. copying the variables bitwise.14 and "e". thus saving an allocation. "Type T2 must be integral"). but soon after the rvalue goes away. T2 t2) ‐> decltype(t1 + t2)  {     static_assert(std::is_integral<T1>::value. But if we add some compile‐time asserts. and then deleting the memory from the temporary when that is destroyed. The copy constructor and the copy assignment operator perform a bit‐wise ﴾or shallow﴿ copy.     size_t               _size. These two special functions take a T&& argument. Hide   Shrink   Copy Code template <typename T>  class Buffer   {     std::string          _name.     std::unique_ptr<T[]> _buffer. You still have to copy its value.  } Hide   Copy Code error C2338: Type T2 must be integral  see reference to function template instantiation 'T2 add<int.  } However. has a pointer ﴾wrapper in an std::unique_ptr﴿ to an array of elements of type T and variable that tells the size of the array. "Type T1 must be integral").5/31/2017 Ten C++11 Features Every C++ Developer Should Use ­ CodeProject {     return t1 + t2. instead of allocating another array.     static_assert(std::is_integral<T2>::value. C++11 has introduced the concept of rvalue references ﴾specified with &&﴿ to differentiate a reference to an lvalue or an rvalue. but for many cases you actually want a deep‐copy. and a later de‐allocation. For instance. Knowing that fact. i. What if the object you initialize or copy from is an rvalue ﴾a temporary﴿.­Cplusplus­Features­Every­Cplusplus­Developer 7/12 . In this case you have to explicitly write copy constructor and copy assignment operator to perform a deep‐copy. both these lines would generate compiler errors. but encourage you to find additional readings.double>(T1. When an object is instantiating from a temporary. This might be OK in some cases. including allocations and memory copying that after all. they just copy the value of the pointers and not the objects they point to. Hide   Copy Code template <typename T1. we just copy the value of the pointer that refers to the allocated array. they can modify the object. 3. The program actually prints 4. typename T2>  auto add(T1 t1. An lvalue is an object that has a name. while an rvalue is an object that does not have a name ﴾a temporary object﴿. there are no compiler errors if one writes Hide   Copy Code std::cout << add(1. Therefore I will not get into too many details. A C++ class/struct used to have some implicit member functions: default constructor ﴾only if another constructor is not explicitly defined﴿ and copy constructor.        T2=int     ] Move semantics This is yet another important and well covered topic from C++11.     return t1 + t2. if you're not already familiar with the topic. That means if you have a class that contains pointers to some objects. not just a paragraph.  public:     // default constructor     Buffer():        _size(16). such as "stealing" the objects their pointers refer to. that one could write a series of articles. The following example shows a dummy buffer implementation. 2) << std::endl.  std::cout << add("one". Enter the move constructor and move assignment operator.

        _buffer(new T[size])     {}     // copy constructor     Buffer(const Buffer& copy):        _name(copy.        }        return *this._size)           {              _buffer = nullptr._size). // assert if this is not a temporary        _buffer = nullptr._size.              _buffer = _size > 0 > new T[_size] : nullptr.     return b.        _buffer(new T[copy.get().           std::copy(source.     }     // move assignment operator     Buffer& operator=(Buffer&& temp)     {        assert(this != &temp).  }  int main()  {     Buffer<int> b1. source + copy.     Buffer<int> b2("buf2"._size])     {        T* source = copy._size._size.  https://www.        temp.        std::copy(source._buffer._buffer.        _buffer(std::move(temp._size =­Cplusplus­Features­Every­Cplusplus­Developer 8/12 ._buffer))     {        temp.     Buffer<int> b4 = getBuffer<int>("buf4").        temp.  template <typename T>  Buffer<T> getBuffer(const std::string& name)   {     Buffer<T> b(name.           if(_size != copy.     b1 = getBuffer<int>("buf5").           T* dest = _buffer.        T* dest = _buffer.           }           T* source = copy.        _size(size)._buffer = nullptr.5/31/2017 Ten C++11 Features Every C++ Developer Should Use ­ CodeProject       _buffer(new T[16])     {}     // constructor     Buffer(const std::string& name._name))._name.     }  }.        _size(copy._size = 0.        _name = std::move(temp.        _buffer = std::move(temp.     }     // copy assignment operator     Buffer& operator=(const Buffer& copy)     {        if(this != &copy)        {           _name = copy.        _size = temp._size._name)._name). dest)._size).        temp.codeproject.get(). 64).        _size(temp._buffer).     }     // move constructor     Buffer(Buffer&& temp):        _name(std::move(temp.get().     Buffer<int> b3 = b2. dest). size_t size):        _name(name).              _size = copy. 128)._buffer = nullptr.get().                return *this. source + copy.

get(). when b1 is assigned a­Cplusplus­Features­Every­Cplusplus­Developer 9/12 ._size  .     public:     // constructor     Buffer(const std::string& name = "". I recommend you additional readings. second. dest).         swap(first._name). when initializing the name variable and the pointer to the buffer.  } The default copy constructor and copy assignment operator should look familiar. i.         swap(first. if we just said _name(temp._name) the copy constructor would have been called.e.     size_t               _size. size_t size = 16):        _name(name)._size. To be easier to see it I will show it here: Hide   Shrink   Copy Code template <typename T>  class Buffer  {     std::string          _name. Also.codeproject. this was just one of many possible beginnings. Buffer& second) noexcept     {         using std::swap. inside the constructor it is actually an lvalue. an rvalue. But why wasn't the move constructor for std::string called in this case? Because even if the object the move constructor for Buffer is called with is an rvalue.         return *this.        T* dest = _buffer.        _buffer(size? new T[size] : nullptr)     {}        // copy constructor     Buffer(const Buffer& copy):        _name(copy. source + copy. Why? Because it has a name.     std::unique_ptr<T[]> _buffer._buffer). An alternative implementation was provided by Member 7805758 in the comments. second. the move assignment operator is called. implemented in the spirit of the aforementioned move semantics. the exact details of an implementation may vary._size] : nullptr)     {        T* source = copy._size). the move constructor is called. is licensed under The Code Project Open License ﴾CPOL﴿ https://www. The name is actually a string. and std::string also implements move semantics. This article presented a series of core language and standard library features that every C++ developer should use._size? new T[copy.5/31/2017 Ten C++11 Features Every C++ Developer Should Use ­ CodeProject    return 0.     }  }. second.get().        std::copy(source. However.         swap(first.     }        friend void swap(Buffer& first. However. If you run this code you'll see that when b4 is constructed. temp).        _buffer(copy.    License This article. copy). To make it again an rvalue ﴾and be able to invoke the appropriate move constructor﴿ one must use std::move._buffer.        _size(size)._name). "temp" and a named object is an lvalue.     }        // move constructor     Buffer(Buffer&& temp):Buffer()     {        swap(*this.    You probably noticed the use of std::move in the move constructor._name  . What's new to C++11 is the move constructor and move assignment operator. This function just turns an lvalue reference into an rvalue reference. The reason is the value returned by getBuffer() is a temporary.        _size(copy._size). Conclusions There are many more things to say about C++11. UPDATE: Though the purpose of this example was to show how move constructor and move assignment operator should be implemented. along with any associated source code and files._buffer.     }        // copy assignment operator     Buffer& operator=(Buffer copy)     {         swap(*this. Same for the std::unique_ptr. For _buffer that would not have been even possible because std::unique_ptr does not have a copy constructor. at least for some of these features.