Department of Engineering

IT Services

C++11

C++ has more than its fair share of detractors. Nevertheless, JAVA and the C family remain the 2 most popular languages according to the October 2010 figures by TIOBE Ranking Index. Like it or not, C++ is here to stay.

The last major version of C++ came out in 1998. C++0x was the working name for the next version, the "0x" part being the last 2 digits of the year it was going to be released in. Work began in 2003. Progress was slow. Meanwhile, a new version of C came out that had features C++ people wanted. In the end the new release of C++ didn't come out until Sept 2011. In Aug 2014 C++14 arrived, which fixed a few anomalies. I'll mention a few of the C++14 features - they're minor. C++17 is the current standard.

Though Stroustrup says it feel like a new language, to me the new version feels like a consolidation - because of the large user/code heritage, they couldn't change too much anyway. There's more uniformity, potential to write faster code, and some hard code's become much tidier to write. Where there's been a choice , the library rather than the core language has been changed, and new keywords have been avoided (e.g. the new final facility isn't reserved).

Sites like cplusplus are already beginning to assume that users have C++11 compilers. In summer 2013, a fully compliant g++ compiler appeared (version 4.8.1) though before that many individual features were available. To run the demos on this page try compiling with

  g++ -std=c++0x ...

if

 g++ -std=c++11 ...

doesn't work. Many of the new features won't affect average programmers directly - they help make advanced code more efficient and (allegedly) make some error messages more legible. I'll start by mentioning some features that undergraduates might use

>>

In the past if you wanted a vector of complex numbers you needed to do

 vector<complex<float> > f; 

Note the space between the 2 ">" signs. It had to be there, otherwise the compiler thought that the bit-shifting operator was being used. In C++11 the space isn't needed! It's a small enhancement that fixes an embarrassing feature.

auto

You no longer have to specify the type of variable you need. You can let the compiler do it for you!

 
int main() {
  auto i = 42;
  auto f = 42.5;
  auto s = "string";
}

In C++2014 you can even do

auto myFunction();

as long as the function has a clear return type (e.g. if it has "int i; .... return i;"). This idea sometimes saves hassle, as we'll see later. But suppose you want to find out what type i is. C++ has always had a way to do this. Let's try it.

 
#include <string> 
#include <iostream> 
#include <typeinfo> 
using namespace std;

int main() {
auto i = 42;
auto f = 42.5;
auto s = "string";

cout << typeid(i).name() << endl; 
cout << typeid(f).name() << endl; 
cout << typeid(s).name() << endl; 
}

I get

i
d
PKc

Feel free to guess what these might mean. In particular, is s a C++ string? The bad news is that there's no spec for what name() returns - it's compiler dependent.

Below is a situation where auto is more clearly useful. I'll show you old and new versions of code

 
#include <string> 
#include <iostream> 
#include <vector>

using namespace std;

int main() {

int vector<int> v;

// Old
for(vector<int>::iterator it = v.begin(); it != v.end(); ++it)
  cout << *it << endl;

// New
for(auto it = v.begin(); it != v.end(); ++it)
  cout << *it << endl;
}

New "for" loop

Range-based "for" loops now exist. The following prints all the values in the array

#include <iostream>
using namespace std;

int main() {
int arr[] = {1,2,3,4,5};
for(int e : arr)
 cout << e << endl;
}

You can use auto and references with this. The following code squares each number in the array

#include <iostream>
using namespace std;

int main() {
int arr[] = {1,2,3,4,5};
for(auto& e : arr)
  e = e*e;
}

Easier initialisation

Initialisation of composite entities has become more standardised. Previously

int i[]={1,2,3};

was possible, but not

vector<int> i={1,2,3};

In C++11 both of these work. Even the following is possible

 
#include <iostream>
#include <vector>
#include <string>
using namespace std;

struct Entry {
    string name;
    long number;
};
 
Entry get_entry() {
    // the next line creates an Entry on-the-fly
    return {"Tim", 332746};
}

int main() {
  Entry s=get_entry();
  cout << s.name<< endl;
}

This is possible too -

...
std::ifstream f;
...
f = {};

The final line default-constructs a temporary object of type decltype(f) and move-assigns it to f (i.e. it resets f)

enum

Old-style C++ enums were like C enums - too much like integers. This was possible -

  enum trees {oak, ash, pine};
  if (oak==3)

and so was this

  enum trees {oak, ash, pine};
  enum verbs {run, cry, tickle};  
  if (oak==tickle)

Being able to mix and compare different types of things isn't pleasant and risks errors. C++11 has strongly-typed enums. You can't compare them with integers or with other enum's values. And there aren't clashes between members of different enums. This is possible

  enum class Options {none, one, all};
  enum class Numbers {one, two, three};
  Options o = Options::all;

Extra features

You may never use the other features but they give me the chance to introduce some more advanced methods.

Lambda Functions

Lambda Functions (sometimes called anonymous functions, or closures) are in many languages, even Matlab. In C++ there were situations where functions had to be written that were only going to be mentioned once elsewhere in the code. For example, to display the elements of a vector using for_each in old C++ you'd need to write a trivial routine first

void display(int n) {
   cout <<n <<endl;  
}

for_each(v.begin(),v.end(), display);

You can use lambda functions like in-line functions. Here's an example

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{
    vector<int> v={1,2,3}; // only possible in C++11
    for_each(v.begin(),v.end(), [] (int n) {cout <<n <<endl;});
}

Here's another example

    auto func = [] () { cout << "Hello world"; };
    func(); // now call the function

lambda functions begin with [], the "capture specification". Then there's an argument list in brackets, then the code. The compiler works out the return type.

See Lambda Functions in C++11 - the Definitive Guide for details.

Multithreading

There's direct support in C++ for this now. It makes possible a type of parallel programming. See http://www.nullptr.me/2011/09/16/multithreading-in-c11-part-1/#.UdkvJOAjAQI where they give 2 examples that I've adapted here. You'll need to add -pthread to the command line to make the executable work.

#include <thread>
#include <iostream>
using namespace std;
 
void myfunction()
{
    cout << "From thread 1" << endl;
}
 
int main()
{
    thread t(myfunction);
    t.join(); // waits. An alternative is to join is detach
 
    cout << "From main thread" << endl;
 
    cin.ignore();
}

The next example uses functors (they're in old C++ too) and lambda functions to show different ways to specify the action of the thread

#include <thread>
#include <iostream>
using namespace std;
 
struct Functor
{
    void operator()() const
    {
        cout << "From functor"  << endl;
    }
};
 
void myfunction()
{
    cout << "From thread 1"  << endl;
}
 
int main()
{
    thread t1(myfunction);
 
    Functor functor;
    thread t2(functor);
 
    auto lambdaFunc = []()->void {cout << "From lambda function" << endl; };
    thread t3(lambdaFunc);
 
    t1.join();
    t2.join();
    t3.join();
 
    cin.ignore();
}

Improved smart pointers

There's no firm definition of "smart pointers" other than they're more sophisticated than ordinary ones. The old C++ had a kind of smart pointer, but in C++11 the choices have been improved. Before you can appreciate the value of smart pointers, you need to know what an ordinary pointer is, and the defects of ordinary pointers.

One situation when pointers might be used is in association with new. In the following line

  int * ip= new int;

a pointer is created to some newly allocated memory. Unlike the memory used to create an integer with

  int i;

this memory isn't recycled when the function it's in exits - it has to be explicitly freed using

  delete ip;

If it's not freed, then there's a "memory leak" - the memory can't be used. If a program does this repeatedly, it will run out of space. With complicated code it's not always easy to ensure that the memory is freed at the right time - too early and you risk a crash if the freed memory is accessed. Another problem arises when 2 pointers point to the same block of memory. After

  int * ip= new int;
  int * ip2= ip;

both ip and ip2 point to the same block of memory, so

 delete ip;
 delete ip2;

(freeing the same block of memory twice) might cause trouble. For the above (and several other) reasons, people have developed more sophisticated types of pointers ("smart pointers"). Here's an example of simple use

#include <memory>
#include <iostream>
using namespace std;

class Foo {
public:
  int i;
  void set(int j) {i=j;};
};

void fun1() {
  Foo a0;
  a0.set(0);
  cout << a0.i << endl;

  Foo *a1= new(Foo);
  a1->set(1);
  cout << a1->i << endl;

  unique_ptr<Foo> a2(new Foo);
  a2->set(2);
  cout << a2->i << endl;
}

int main() {
  fun1();
}

In fun1 3 objects of type Foo are created. The memory associated with a0 is recycled once fun1 finishes, because a0 is local to fun1. The space to store the a1 pointer is recycled, but NOT the space for the Foo variable it points to. Each time the fun1 function is called, that memory is used up. In this case, the Foo object isn't big, but in other cases it could be. All the space associated with a2 is recycled, because a smart pointer is used.

For details, see

Misc

Just for the record, the new keywords are (I think) alignas, alignof, char16_t, char32_t, constexpr, decltype, noexcept, nullptr, static_assert, thread_local. export has more or less gone. There are some new features that you're unlikely to use but you might see, so it's worth knowing that they exist

  • Defining something as a "constexpr" means that it can potentially be evaluated at compile time rather than while the program's running, thus speeding up the program. Alex Allain gives a simple example -

    constexpr int multiply (int x, int y)
    {
        return x * y;
    }
    
    const int val = multiply( 10, 10 );
    
  • Beginners sometimes try things like "int thousand=1,000;" (note the comma). In C++14 something like that is possible. E.g.
      auto thousand=1'000;
    
  • In C++14 literal integers can be expressed in binary (at last), so "int i=0b101;" is possible.
  • Pre-C++2014, functions and classes could be templated but not variables. That anomaly has now been fixed.
  • A new type of array that has the compactness of C arrays and the friendliness of C++ vectors (except that they don't change size). This is possible
      array<int,3> a3 = { 1, 2, 3 };
      a3[0]=7;
      cout << sum(a3) << endl;
    
    But this isn't
    array<int,3> a3 = { 1, 2, 3 }; // ok
    int* ptr= a3; // No. use int* ptr= a3.data();
    
  • There's an easy way to initialise arrays with sequences of values -
    #include <iostream>
    #include <numeric>
    using namespace std;
    
    int main (int argc, char** argv) {
    int a[5];
    char c[3];
    iota(a, a+5, 10);
    iota(c, c+3, 'a');
    cout << "a=";
    for (int i=0;i<5;i++)
       cout << a[i] << " ";
    cout << endl << "c=";
    for (int i=0;i<3;i++)
       cout << c[i] << " ";
    cout << endl;
    return 0;
    }
    
  • A new type of constructor - as well as the constructors to create an object from scratch and copy an existing object, there's now a "Move constructor"
    classname (const classname &obj) {
       // copy constructor
    }
    
    classname (const classname &&obj) {
       // move constructor
    }
    
    The move constructor uses a new type of reference - called an rvalue reference. This make it possible to write more efficient code in some situations. To take a simple example where this might be the case, suppose we had a function that given a string returned the reverse of the string. In the function there might be
    string answer; // create a string object
    //set answer to something
    ...
    return answer;
    

    The routine might be called by doing

    string t;// create a string object
    t=reverse(s);
    

    When the function returns, the fields of "answer" are copied into the fields of "t" then "answer" is destroyed. More efficient would be to keep "answer" but somehow rename it "t", then no copying would be necessary. In this simple example, use of old C++ references (now called "lvalue references") would help, and besides, strings aren't big objects, but if we were dealing with images or matrices and there are intermediate RHS values the benefits of this approach might be considerable. For example, matrix calculations like a=(b*c)*d involve many temporary objects that are expensive to copy.

    On the cppreference.com there's an example that uses the std::move function. It compiles with gcc version 4.8.5's "-std=c++11" option

    #include <iostream>
    #include <utility>
    #include <vector>
    #include <string>
     
    int main()
    {
        std::string str = "Hello";
        std::vector<std::string> v;
     
        // uses the push_back(const T&) overload, which means 
        // we'll incur the cost of copying str
        v.push_back(str);
        std::cout << "After copy, str is \"" << str << "\"\n";
     
        // uses the rvalue reference push_back(T&&) overload, 
        // which means no strings will be copied; instead, the contents
        // of str will be moved into the vector.  This is less
        // expensive, but also means str might now be empty.
        v.push_back(std::move(str));
        std::cout << "After move, str is \"" << str << "\"\n";
     
        std::cout << "The contents of the vector are \"" << v[0]
                                             << "\", \"" << v[1] << "\"\n";
     
        // string move assignment operator is often implemented as swap,
        // in this case, the moved-from object is NOT empty
        std::string str2 = "Good-bye";
        std::cout << "Before move from str2, str2 = '" << str2 << "'\n";
        v[0] = std::move(str2);
        std::cout << "After move from str2, str2 = '" << str2 << "'\n";
    }
    

    For details see Danny Kalev's tutorial or stackoverflow.

  • You can specify the return value of a function in a different way - at the end of the definition rather than at the beginning.
    auto multiply (int x, int y) -> int;
    
  • New types -
    • A new <cstdint> header includes types such as std::int8_t, std::int64_t, etc. Previously there was no standard way to select particular integer sizes. Also, long long exists - at least 64 bits.
    • More types of literal string exist - e.g. U"This is a Unicode Character: \U00002018." has UTF-32 Unicode characters (so each character occupies 4 bytes).
  • The standard library can now deal with regular expressions (though they're not supported in many implementations)
  • The standard library has support for some standard statistical distributions - the example below is adapted from wikipedia
    #include <random>
    #include <functional>
    #include <iostream>
    using namespace std;
    int main() {
      uniform_int_distribution<int> distribution(0, 99);
      mt19937 engine; // Mersenne twister MT19937. Generates random numbers
      auto generator = bind(distribution, engine); // bind creates a functor
      int random = generator(); // Generate a uniform integral variate between 0 and 99.
      int random2 = distribution(engine); // Equivalent to the above line
    
     // Now let's try a normal distribution
      normal_distribution<> heights(6,2);
    
      for (int i=0;i<10;i++) {
        cout << heights(engine) << endl;
      }
    }
    

Compilers

  • g++ - the C++0x/C++11 Support in GCC page shows the level of support for the various C++ features. Our local version (4.8.5) implements all of the major features of the C++11 standard except for regular expressions. We've installed g++5 which has better support. GCC 8 has complete (but experimental) support for C++17.
  • clang - this is used by Macs' Xcode. The C++14, C++11 and C++98 Support in Clang page shows the level of support (which is similar to - and in part dependent on - g++ development). Clang 5 and later implement all the features of C++17.
  • According to wikipedia, Visual Studio 2017 15.7 (MSVC 19.14) supports almost all of C++17.

See Also