Top 10 C++14 Features – Out Now!



Top 10 C++14 Features – Out Now!

0 1


cxx14-otris

C++14 slides

On Github bkircher / cxx14-otris

Top 10 C++14 Features

Out Now!

Slides by Ben / Code samples by Coliru / Layout by reveal.js

Every example that follows compiles with

  • Visual Studio 2012
  • gcc 4.8

Ahhh, time is running so fast

C++11 C++14 Lots of new stuff Only minor corrections unique_ptr, shared_ptr make_unique<T>() lambdas polymorphic lambdas

Shiny New Things

auto specifier initializer lists range based for-loops lambdas unique_ptr shared_ptr unordered_map control of defaults: default and delete raw string literals nullptr

0. My favourite

0.My favourite: right-angle brackets!

>> illegal in C++98

int main()
{
    std::vector<std::vector<std::string> > vec;

    // Now you can write:
    std::vector<std::vector<std::string>> vec2;
}

1. auto specifier

1. auto specifier

All about type inference

          
            Syntax:
            auto variable initializer
            auto function -> return type
          
          

1. auto specifier

Specifies that the type of the variable that is being declared will be automatically deduced from its initializer.

auto i = 42;
auto u = 1729U;
auto str = std::string("Mehr Bier");

1. auto specifier - why should I use it?

If you are lazy

struct Beer
{
    Beer(const char* s) : name(s) {}

    std::string name;
};

int main()
{
    std::vector<Beer> vec{ "Thier", "Bergmann", "Kronen" };

    for (std::vector<Beer>::iterator it = vec.begin(); it != vec.end(); ++it)
    {
        std::cout << it->name << "-Pils\n";
    }
}

Edit & run

1. auto specifier - why should I use it?

If you want to avoid bugs

char buf[20];
std::istringstream stream("Hello, there!");
stream.read(buf, sizeof buf);
// Conversion from std::streamsize to size_t
size_t bytes_extracted = stream.gcount();
std::cout << "Characters extracted " << bytes_extracted << '\n';
          

Edit & run

1. auto specifier

template<class T, class U>
auto add(T t, U u) -> decltype(t + u)
{
  return t + u;
}

Edit & run

2. initializer lists

2. initializer lists

All about uniform initialization

{ }

2. initializer lists

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

int main()
{
    std::vector<double> vec{ 1, 2, 3.456, 99.99 };
    std::unordered_map<std::string, std::string> beers{
        {"Thier","Dortmund"},
        {"Krombacher","Kreuztal"},
        {"Fiege","Bochum"}
    };
    for (const auto& beer : beers)
        std::cout << beer.first << ", " << beer.second << std::endl;
}
            

Edit & run

Supported since November 2012 CTP

2. initializer lists - implement this ourselves

#include <initializer_list>

template<class T> class ListJ
{
public:
    // initializer-list constructor
    ListJ (std::initializer_list<T> s)
    {
        // get the right amount of space
        reserve(s.size());

        // initialize elements
        uninitialized_copy(s.begin(), s.end(), elem);

        // set size
        sz = s.size();
    }

    // ...
};
          

3. range-for statement

3. range-for statement

Iterate through a range

which is anything with a begin() and end()

or with a begin() and length

3. range-for statement

int main()
{
    std::vector<std::string> vec{"Bergmann", "Union Export", "Fiege"};
    for (auto& beer : vec) // access as reference to change a value
    {
        std::for_each(begin(beer), end(beer),
            [](char& c) { c = std::toupper(c); });
    }
    // access as const-ref to avoid copying
    for (const auto& beer : vec) std::cout << beer << '\n';
    for (auto x : { 11,22,33,44,55,66,77 }) std::cout << x << '\n';
}
          

Edit & run

4. lambda expressions

4. lambda expressions

An anonymous function (a function without a name, thats all)

Can have an optional capture clause (that makes it a closure)

          
            Syntax:
            [capture-list] (params) {body}
            [capture-list] {body}
            [capture-list] (params) -> ret {body}
            [capture-list] (params) mutable {body}
          
          

4. lambda expressions

int main()
{
    std::vector<int> vec{ 1,2,3,4,5,6,7 };
    int x = 4;
    vec.erase(std::remove_if(vec.begin(), vec.end(),
            [x](int n) { return n < x; } ), vec.end());

    std::cout << "vec: ";
    for (auto i: vec) {
        std::cout << i << ' ';
    }
    std::cout << '\n';
    // The type of a closure cannot be named, but can
    // be inferred with auto
    auto add4 = [](int i) { return i+4; };
    std::cout << "add4: " << add4(6) << '\n';
}
          

Edit & run

4. lambda expressions - capturing explained

Access variables from outer scope (and optionally modify them)

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

int main()
{
    std::vector<std::string> vec{ "Dortmunder Actien-Brauerei" };
    auto push_to_vec = [](const std::string& beer)
    {
        vec.push_back(beer);
    };
    for (const auto& b : { "Hansa", "Bergmann", "Thier" })
    {
        push_to_vec(b);
    }
    for (const auto& b : vec) std::cout << b << '\n';
}
          

Edit & run

4. lambda expressions - capturing explained

          
          capture-list:
          [a,&b] a is captured by value, b is captured by ref
          [this] captures this pointer by value
          [&] captures all variables used in body of lambda by ref
          [=] captures all variables used in body of lambda by value
          [] captures nothing
          
          

That's it with the language for today

Now on to the quest on getting rid of owning raw-pointers: Smart pointers!

          new, delete
          new[], delete[]
          

5. unique_ptr<T>

5. unique_ptr<T>

  • Replacement for auto_ptr, which is now deprecated
  • Is movable, not copyable
  • (Thanks to rvalue-references and move-semantics)
  • You can put it into a vector
  • Helps immensely writing exception-safe code
  • Nearly no performance overhead

5. unique_ptr<T>

#include <memory>

struct Foo
{
    Foo() { std::cout << "Foo::Foo\n";  }
};

int main()
{
    std::unique_ptr<Foo> p1(new Foo);  // p1 owns Foo

    // Foo instance is destroyed when p1 goes out of scope
}
          

Edit & run

6. shared_ptr<T>

6. shared_ptr<T>

  • Reference-counting smart pointer
  • Shared ownership
  • Object is deleted when last reference to it goes out of scope
  • Little performance overhead, mitigated with make_unique

6. shared_ptr<T>

#include <memory>

int main()
{
    {
        std::shared_ptr<int> p1(new int); // count is 1
        {
            std::shared_ptr<int> p2(p1); // count is 2
            {
                std::shared_ptr<int> p3(p1); // count is 3
            } // count goes back down to 2
        } // count goes back down to 1
    } // here the count goes to 0 and the int is deleted.
}
          

Edit & run

7. unordered_map<T>

Hash tables, yeah :/

#include <unordered_map>
          

7. unordered_map<T>

template<
    class Key,
    class T,
    class Hash = std::hash<Key>,
    class KeyEqual = std::equal_to<Key>,
    class Allocator = std::allocator< std::pair<const Key, T> >
> class unordered_map;
          

7. unordered_map<T>

  • K -> V
  • associative container
  • contains key-value pairs with unique keys
  • much like std::map but not sorted, values kept in buckets
  • usually yields much faster look-up than std::map

8. = default and = delete

Control of defaults

8. = default and = delete

Remember?

class Foo
{
public:
    // ...

private:
    // Never defined
    Foo(const Foo&);
    Foo& operator=(const Foo&);
};
          

8. = default and = delete

Now you can just write:

class Foo
{
public:
    Foo(const Foo&) = delete;
    Foo& operator=(const Foo&) = delete;

    // ...
};
          

Edit & run

8. = default and = delete

We can delete an undesired conversion like this:

struct Foo
{
    // ...
    Foo(long long); // can initialize with an long long
    Foo(long) = delete; // but not anything less!
};
          

9. Raw string literals

const char* str = R"("quoted string")";
// the string is "quoted string"
          

9. Raw string literals

Very useful if you have inline SQL or regexp.

Motivating example:

#include <iostream>
#include <regex>

int main()
{
    const char* pat = R"((\+|-)?[[:digit:]]+)";
    std::regex integer_pattern(pat);
    const char* input = "-123";
    std::cout << (std::regex_match(input, integer_pattern) ?
        "matches" : "does not match") << std::endl;
}
          

Edit & run

10. Last but not least: nullptr

10. nullptr

A null pointer literal

Yes, it has a type: std::nullptr_t

10. nullptr

Contrary:

#define NULL 0
          

Actually, implementation defined, but pretty likely

10. nullptr

void f(int* pi) { }

void f(double* pd) { }

void f(std::nullptr_t nullp) { }

int main()
{
    int* pi; double* pd;

    f(pi);
    f(pd);
    f(nullptr);  // would be ambiguous without void f(nullptr_t)
    // f(NULL);  // ambiguous overload
}
          

Edit & run

10. nullptr

Pretty easy to transform your code base, too ;)

$ sed -i `s/NULL/nullptr/g` *.{h,cpp}

C++14 has much more to offer!

  • static_assert (compile-time assertions)
  • <thread>
  • <atomic> (sane memory model)
  • <tuple>
  • variadic templates
  • std::function
  • move semantics (lets you make non-copyable types)
  • override controls: override and final
  • enum class (safer enum, at least a little)
  • constant expressions: constexpr
  • user-defined literals
  • attributes like [[noreturn]]
  • ...

Pointers

http://www.stroustrup.com/C++11FAQ.html

http://en.cppreference.com/w/

Top 10 C++14 Features Out Now! Slides by Ben / Code samples by Coliru / Layout by reveal.js