Nax.io

C++11 Is Awesome

August 31, 2014

C++11 is an awesome extension to the C++ standard. It allows you to write portable, powerful and extensible code with ease. Here are the top features of C++11.

Auto

The auto keyword was introduced in C, as a specifier for variables, like register. It was an implicit specifier, telling the compiler that a given variable should have automatic storage. Every variable already had automatic storage unless told otherwise, making this keyword useless.

In C++11, the meaning of auto was changed. Now the auto keyword is used instead of a type in a variable declaration, to automatically deduce type.

int    i = 5;
float  f = 42.0f;
double d = 0.0;

// C++11

auto   i = 5;      /* i is an int   */
auto   f = 42.0f;  /* f is a float  */
auto   d = 0.0;    /* d is a double */

It may seem pretty useless, but can be helpful when dealing with complex template types.

#include <iostream>
#include <list>

int main()
{
    std::list<std::string> l;

    l.push_back("Hello !");
    l.push_back("How are you today ?");
    l.push_back("I'm fine, thanks.");

    for (auto it = l.begin(); it != l.end(); ++it)
        std::cout << *it << std::endl;
}

auto is used as a shortcut for std::list<std::string>::iterator in the previous example. Code less, express more!

Lambdas

Lambdas, sometimes called closures, are a way to declare a pseudo-function, without giving it a name. A Lambda can be declared anywhere, like in another function body, or even in another lambda. They are a must-have when dealing with functional programming.

Consider the previous example, we can rewrite it using a nice lambda.

#include <iostream>
#include <list>
#include <algorithm>

int main()
{
    std::list<std::string> l;
    auto fn = [](std::string& s){ std::cout << s << std::endl; };

    l.push_back("Hello !");
    l.push_back("How are you today ?");
    l.push_back("I'm fine, thanks.");

    std::for_each(l.begin(), l.end(), fn);
}

We created a lambda printing a string, assigned it to a variable named fn (using auto for type deduction), and passed it as a parameter to std::for_each.

A lambda is defined by:

  • A pair of brackets, containing capturing rules (see below)
  • An optional parameter list
  • A pair of curly braces, with the lambda source code

Lambdas does not execute in the same scope as their parent functions. However, you may still want to access some local variables within the parent function from the lambda. The best way to do so is adding capturing rules to the lambda.

Variables can be captured by value or by reference. Capturing follow the same rules as argument passing to a function: passing by value imply calling the copy constructor, while passing by reference is just pointer magic.

#include <iostream>

int main()
{
    int i = 5;

    [&]{ i = 42; }();
    std::cout << i << std::endl; // 42
}

The capturing rule used is [&], or capture-by-reference. It can be followed by a variable name to restrict capturing to this variable only, or left alone to capture the entire local scope.

Capturing by reference is often the best choice when dealing with large structs or objects, or when needing to alter a variable from the lambda. When dealing with primitive types, it may be more appropriate to use the capture-by-value operator, denoted by [=].

Threads

Threading is an important element to consider when writing complex applications nowadays. However, it was impossible to create portable multithreaded applications in bare C++, because each platform have it's own threading library. With C++11, this is no more a concern.

The new standard library ships with std::thread, an abstraction of the different threading libraries used by each platform, as well as some classes to make threading much more simple, like std::mutex.

#include <iostream>
#include <thread>
#include <mutex>

std::mutex g_mutex;

void fn()
{
    for (int i = 0; i < 100000; i++)
    {
    g_mutex.lock();
    std::cout << i << std::endl;
        g_mutex.unlock();
    }
}

int main()
{
    std::thread t(fn);
    fn();
    t.join();
}

In this example, a new thread is spawned, executing the fn function, while the main thread executes fn too. Both thread run in parallel, but the global mutex prevent them from printing to the screen at the same time, because std::ostream::operator<< is not thread safe.

There is much, much more to say about threads. You can wait for a thread to finish using join, or detach the thread using detach. You can also make a thread sleep using std::this_thread::sleep_for.

User-defined literals

User-defined literals are a neat feature that allows to declare virtually anything as a literal.

#include <iostream>
#include <cmath>

constexpr long double operator"" _deg(long double d)
{
    return (d * M_PI / 180.0);
}

int main()
{
    double angle = 238.74_deg;

    std::cout << angle << std::endl; // 4.1668
}

User-defined literals can return any type, and can be used for strings, too. One can also interpret the literal as a string, regardless of it's type. This is useful when creating literals for a Bignum class.

…And much more

C++11 introduces a lot more features than that, like move semantics, range-based for, and much, much more. Thanks to this new standard, developers can write elegant and modern code that suit their needs for their applications.

Learn more about C++11 Here.

comments powered by Disqus