STL11

Sven Johannsen
09.11.2013
Meeting C++ Düsseldorf

sven@sven-johannsen.de
www.sven-johannsen.de
@svenjohannsen

Content

Motivation

A short example for the erase-remove idiom

C++ 98/03
vector<int> v;
v.push_back(0); v.push_back(5); v.push_back(2); v.push_back(3);

// use the erase-remove idiom to remove all elements with the value 5
v.erase(remove(v.begin(), v.end(), 5));

v = vector<int>(v.begin(), v.end()); // free unused capacity
C++ 11
vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// vector<int> v(10); iota(begin(v), end(v), 0);

// use the erase-remove idiom to remove all elements with the value 5
v.erase(remove(begin(v), end(v), 5));

v.shrink_to_fit();        // free unused capacity

History (STL)

C++98 / C++03

  • vector, list, ...
  • iterators
  • algorithm
  • string
  • iostream
  • ...

C++11 Language change

  • R-Values / Move semantic
  • uniform initialization

TR1

(2005)

  • boost subset
  • unordered container
  • random
  • C99 header wrapper

C++11 Standard library

  • concurrency
  • unique_ptr
  • exception
  • forward_list
  • extend algorithm
  • non-member begin() / end()

Boost Subset 1

(2000-...)

  • shared_ptr
  • tuple
  • array
  • function, bind, mem_fn, ref, result_of

Boost Subset 2

  • (Thread)
  • (scoped_ptr)
  • (exception)

What's new in the STL?

Uniformed initialization

Problem: Different syntaxes for initializing

    struct A { int i; int j; };
    struct B { B(int ii, int jj); /* ... */ };

C++98

    A a = { 1, 3 };
    B b(1, 3);

C++11 Uniform initialization (N2532)

// Aggregates (e.g. arrays and structs):
A a1 = { 1, 3 }; // Initialize  members from begin-to-end

// Non-aggregates: Invoke a constructor.
B b1 = { 1, 3 };

// alternative syntax
A a2 { 1, 3 };
B b2 { 1, 3 };

Uniformed initialization for containers

C++98

int arr[] = {1, 2, 3};
vector<int> v;
v.push_back(1); v.push_back(2); v.push_back(3);

C++11

Initializer Lists (N2672)

#include <initializer_list>

template <class T> // ignoring allocators
class vector {
    // ...
    vector(initializer_list<T>);   // initializer list constructor
    vector(size_type n, const T& value); // other constructor
    //...
};

vector<int> vec1 = { 10, 2 }; // vector::vector(initializer_list<T>);
vector<int> vec2(10, -1);     // vector::vector(size_type n, const T& value);

STL & Initializer Lists

All STL containers support uniformed initialization

vector<int> v({ 2, 3, 5, 7, 11, 13, 17 });
list<int> l = { 0, 1, 2, 3, 4, 5, 6 };
map<int, string> m { { 1, "one" }, { 2, "two" } };
valarray<double> v = { 1.0, 0.1, 0.001, 0.0001 };
// same for deque, forward_list, set, string, regex
// unordered_map, unordered_set, multi_..
// but not for: queue, priority_queue and stack

// aggregate initialization
array<double,4> a = { 1.0, 0.1, 0.001, 0.0001 };

and some algorithms too

More than initialization

Most containers overload some additional member functions for initializer_list<T>.

vector<int> v = { -1, -2, -3 }; // v = -1, -2, -3
v = { 1, 2, 3 };                      // operator=() v = 1, 2, 3
v.insert(end(v), { 4, 5, 6 });        // v = 1,2,3,4,5,6
v.assign({ -1, -2, -3 });             // v = -1, -2, -3

(All STL containers = {string, deque, forward_list (insert_after()), list, vector, map, multimap, set, multiset, unordered_map, unordered_multimap, unordered_set, unordered_multiset })

Class initializer_list<class E>

#include <initializer_list>
...
template<class E> class initializer_list {
public:
  // some typedefs

  initializer_list() noexcept;     // default constructor
  size_t size() const noexcept;    // number of elements
  const E* begin() const noexcept; // first element
  const E* end() const noexcept;   // one past the last element
};
Only the compiler should fill the Initializer Lists.
initializer_list<string> strings = { "C++", "is", "cool!" };

Using the Initializer lists

Using the class initializer_list in user defined containers
template<class T>
MyVector<T>::MyVector(initializer_list<T> i_list)
{
    reserve(i_list.size());

    for(auto iter = i_list.begin(); iter != i_list.end(); ++iter)
        push_back(*elem);

    // for(const auto& T elem : i_list)
    //   push_back(elem);
}

// usage
MyVector<int> v = { 2, 3, 5, 7 };

Compile time container

initializer_list is not limited to container

void print_some_doubles(initializer_list<double> doubles)
{
    for(double d : doubles)
        cout << d << " ";
}

...

print_some_doubles({ 1, 2, 3 });

R-Value References support in the STL

Problem: Performance!

Copy semantic can result in performance issues.

vector<string> v;
v.push_back("C++");
v.push_back("Boost");
// Hint: emplace_back beats move semantic

C++11 introduce move semantic into the language to reduce the number of new / delete calls.

Temporary objects ("objects without a name"):

Move Semantic

How to move?
  1. Detect temporary objects (r-values)
        basic_string& operator=(const basic_string& str);     // l-value reference
        basic_string& operator=(basic_string&& str) noexcept; // r-value reference
    
  2. Steal the content from the temporary object
  3. Bring the temporary objects in a stable state

Designed for objects which uses of dynamic memory, like STL containers

Looks like moving the objects, but only the content is moving

R-Value References everywhere

Addional overloads for r-value references

vector:

vector<string> v;
string text("C++");

v.push_back(text);  // copy "C++"
v.push_back("Use the boost library!"); // create a temp. string object

STL overloads many functions for improving the performance.
E.g. 12 different operator+() for the combination of string&, string&& and char*

Enforce moving

The C++ and the STL "moves" only temporary objects ("objects without a name")

To move non-temporary objects, use std::move() to mark an object as r-value reference.

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

result = std::move(temp);
//result = static_cast<vector<int>&& >(temp);

assert(temp.size() == 0);
assert(result.size() == 3);
assert(result[0] == 1 && result[1] == 2 && result[2] == 3);

Move non-copyable objects

Examples:

ifstream open_file(const string& filename) { ... }
unique_ptr<MyDocument> document_factor(Param param) { ... }

vector<unique_ptr<MyDocument>> documents;
documents.push_back(document_factor(param));

Emplace

(deque, list, vector, priority_queue)

Construct an element in the container. Forward all parameters to the constructor.

template <class... Args>
void emplace_back(Args&&... args);

template <class... Args>
iterator emplace(const_iterator position, Args&&... args);

Example:

vector<string> field= { " " };
field.emplace_back("C++");
char* text = "Hallo";
field.emplace(field.begin(), text, text+6);
// field == "Hello", " ", "C++"

Containers

TR1
C++ 11
Changes for exiting containers

array

As efficient as a "C style" array, but with the interface of a STL container

int field[3] = { 1, 2, 3 };
array<int, 3> arr = { 1, 2, 3 };

cout << "size : " << arr.size() << endl;
for (auto it = arr.begin(); it != arr.end(); ++it)
  cout << *it << " ";
cout << endl;

array

template <class T, size_t N>
struct array {
  // some typedefs
  // no constructor, no operator=() !

  void fill(const T& u);

  iterator begin() noexcept;
  iterator end() noexcept;
  // rbegin(), rend(), cbegin(), cend(), crbegin(), crend()

  constexpr size_type size() noexcept; // max_size, empty

  reference operator[](size_type n);
  reference at(size_type n);
  reference front();
  reference back();
  T* data() noexcept;
  // plus const functions
};

Examples

array<int, 3> arr {}; // zero initialization

arr.fill(-11);
for (auto i : arr)
  assert(i == -11);

iota(arr.begin(), arr.end(), 1);

int i = arr[0];
int j = arr.at(1);
int k = arr.back(); // last element
assert(i = 1 && j == 2 && k == 3);

int l = arr.at(10); // will throw an "out of range" exception

Unordered associative containers

4 new hash_maps (associative containers):

Similar to map, multimap, set and unordered_multiset, with different requirements for the key and different storage. The naming tries to avoid breaking existing code.

hash_map

Unordered associative containers

replacement
map<string, int> index = { { "C++", 1 }, { "Boost", 42 } };

unordered_map<string, int> fast_index = { { "C++", 1 }, { "Boost", 42 } };

Unordered associative containers

Hash and equality requirement for the key
map<string, int> index;

// same as: 
map<string, int, less<string> > index;

struct PersonLess
{
  bool operator()(const Person& l, const Person& r)
  { 
    return l.Name() < r.Name();
  }
};

map<Person, Account, PersonLess> AccountInfo;

Unordered associative containers

Requirements for the key
unordered_map<string, int> fast_index;

// same as:
unordered_map<string, int, hash<string>, equal_to<string> > fast_index;

struct PersonHash
{
  size_t operator()(const Person& p)
  {
    return hash<string>()(p.Name());
  }
};

// for the case of collitions
struct PersonEquality
{
  bool operator()(const Person& l, const Person& r)
  {
    return l.Name() == r.Name();
  }
};

unordered_map<Person, Account, PersonHash, PersonEquality> FastAccountInfo;

Unordered associative containers

Predefined hash functions

Hash functions are available for

forward_list

Minimal list implementation, which avoid expensive operations (e.g. back()).

forward_list

forward_list

template <class T, class Allocator = allocator<T> >
class forward_list {
public:
// some typedefs
  explicit forward_list(); // 9 constructors
  forward_list& operator=(initializer_list<T>); // +3

  iterator begin() noexcept;
  iterator end() noexcept; // + cbegin, ... but no rbegin()

  bool empty() const noexcept; // no size()

  void push_front(const T& x);
  void pop_front();

  iterator insert_after(const_iterator position, const T& x); // + 5
  // ...
  void sort();
  void reverse() noexcept;
};

insert_after()

STL container member functions like insert() need the access to the prior element.

forward_list

Further Changes for exiting containers (a)

const_iterator

cbegin, cend, crbegin, crend

const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;

Better control of the used iterator type

void foo(const vector<int>& cv, vector<int>& ncv)
{ // C++98
  for (vector<int>::const_iterator it1 = cv.begin();  it != cv.end();  ++it) {}
  for (vector<int>::const_iterator it2 = ncv.begin(); it != ncv.end(); ++it) {}

  // C++11
  for (auto it = cv.begin();  it1 != cv.end();  ++it) {} // const_iterator
  for (auto it = ncv.begin(); it2 != ncv.end(); ++it) {} // iterator

  for (auto it = ncv.cbegin(); it3 != ncv.cend(); ++it) {} // const_iterator

Further Changes for exiting containers (b)

capacity

(string, deque, vector)

void shrink_to_fit();

Ask for reducing capacity() to size().

Example:

// free unused capacity with a temp. object
vector<int> tmp(v.begin(), v.end());
v.swap(tmp);

// free unused capacity
v.shrink_to_fit();

Further Changes for exiting containers (c)

data access

(vector, array)

T* data() noexcept;
const T* data() const noexcept;

Return the address of the first element.

Example:

vector<BYTE> field = ...;
legacy_function(BYTE* raw_data, int size);
...
// C++98: address of the first element
legacy_function(field.empty() ? NULL : &field[0], field.size());
legacy_function(field.empty() ? NULL : &field.front(), field.size());

// C++11 use data
legacy_function(field.data(), field.size());

Further Changes for exiting containers (d)

map::at()

Element access with range check (throws, if the key is not present).

map<string, int> cont;
int val;

// C++ 98
val = cont["key"]; //(1)  may add a default value to the map

auto it = cont.find("key");
if(it != cont.end()) //(2)
    val = it->second;

// C++11
val = map.at("key"); //(3) throws "out_of_range", if key not present

Iterators

Range access

Non Member begin() and end()

Unified iterator access for any container

Addition level of abstraction for an iterator access.

    // vector<int> cont = { ... };
    // int cont[] = { ... };

    for(auto it = begin(cont); it != end(cont); ++it)
    {
        cout << *it << " ";
    }

This code runs with any container (if non-member begin() and end() are overloaded).

C++14 will also introduce non-member cbegin, cend, rbegin, rend, crbegin and crend.

New Algorithms

A-M N-Z
all_of (is p true for all e in R?) is_partitioned (is R partitioned per p?)
any_of (is p true for any e in R?) partition_point (find first e in R where p(e) is false)
none_of (is p true for no e in R?) is_sorted (is R sorted?)
find_if_not (find first e in R where p is false) is_sorted_until (find first out-of-order e in R)
copy_if (copy all e in R where p is true) is_heap (do elements in R form a heap?)
copy_n (copy first n elements of R) is_heap_until (find first out-of-heap-ordered in R)
iota (assign all e in R increasing values starting with v) move (like copy, but each e in R is moved)
minmax (return pair(minVal, maxVal) for given inputs) move_backward (like copy_backward , but each e in R is moved)
minmax_element (return pair(min_element, max_element) for R) partition_copy (copy all e in R to 1 of 2 destinations per p(e))


R is a range, e is an element, p is a predicate.

(note: use <numeric> for iota)

std::string

string to number conversion

C++ wrapper around strtol(), ...

int stoi(const string& str, size_t *idx = 0, int base = 10);
long stol(const string& str, size_t *idx = 0, int base = 10);
unsigned long stoul(const string& str, size_t *idx = 0, int base = 10);
long long stoll(const string& str, size_t *idx = 0, int base = 10);
unsigned long long stoull(const string& str, size_t *idx = 0, int base = 10);

float stof(const string& str, size_t *idx = 0);
double stod(const string& str, size_t *idx = 0);
long double stold(const string& str, size_t *idx = 0);

number to string conversion

and back into a string

string to_string(int val);
string to_string(unsigned val);
string to_string(long val);
string to_string(unsigned long val);
string to_string(long long val);
string to_string(unsigned long long val);
string to_string(float val);
string to_string(double val);
string to_string(long double val);

Examples

stoi()
string text{ "3E8" };
int val{};

try
{
  size_t index; // no initialization needed, only output!
  val = stoi(text, &index, 16);
}
catch (const exception& ex)
{
  cout << "Error : " << ex.what() << endl;
}
assert(val == 1000);
to_string()
wstring text = to_wstring(15);
wcout << text << endl;

Smart-Pointers

Memory management can be tricky
void f()
{
  // init
  std::string name("A . . . long . . . Name");
  MyObject* p = new MyObject(15);
  OtherObject* q = getOtherObject("Bla");

  try {
      maybeThrowing(p); // may throw an exception, will be handled outside
      otherFunction(q)
  }
  catch(...) {
      delete q;
      delete p;
      throw;
  }
  delete q;
  delete p;
}

Smart-Pointers

TR1

C++11

C++14

Smart-Pointers

shared_ptr
unique_ptr

unique_ptr

C++98C++11
MyObject* foo()
{
  string name("A...long...Name");
  MyObject* p = new MyObject(15);
  OtherObj* q = getOtherObj("Bla");

  try {
      maybeThrowing(q);
      otherFunction(p)
  }
  catch(...) {
      delete q;
      delete p;
      throw;
  }
  delete q;
  return p;
}
shared_ptr<MyObject> foo()
{
  string name("A...long...Name");
  auto p = make_shared<MyObject>(15);
  unique_ptr<OtherObj> q(getOtherObj("Bla"));

  maybeThrowing(q.get());
  otherFunction(p)

  return p;
}

make_shared

Create the created the shared_ptr's control block and the object with one single memory allocation.

struct DBConnec {
  DBConnec(string dbname, string server, string securityInfo){}
};

// TR1
shared_ptr<DBConnec> dbConn1(new DBConnec("southwind", "localhost", "SSPI"));

// C++11
unique_ptr<DBConnec> dbConn2(new DBConnec("storm", "192.168.10.11", "none"));

// make_shared (C++11)
auto dbConn1 = make_shared<DBConnec>("southwind", "localhost", "SSPI");

// make_unique (C++14)
auto dbConn2 = make_unique<DBConnec>("storm", "192.168.10.11", "none");

new Libraries

this_thread::sleep_for(std::chrono::milliseconds(123));

Compile-time rational arithmetic

ratio

A template class which exactly represents any finite rational number with a numerator and denominator representable by compile-time constants

template <intmax_t N, intmax_t D = 1>
class ratio {
public:
  typedef ratio<num, den> type;
  static constexpr intmax_t num;
  static constexpr intmax_t den;
};

ratio

typedef ratio<1, 1000000000000000000000000> yocto; //see below
typedef ratio<1, 1000000000000000000000> zepto; //see below
typedef ratio<1, 1000000000000000000> atto;
typedef ratio<1, 1000000000000000> femto;
typedef ratio<1, 1000000000000> pico;
typedef ratio<1, 1000000000> nano;
typedef ratio<1, 1000000> micro;
typedef ratio<1, 1000> milli;
typedef ratio<1, 100> centi;
typedef ratio<1, 10> deci;
typedef ratio< 10, 1> deca;
typedef ratio< 100, 1> hecto;
typedef ratio< 1000, 1> kilo;
typedef ratio< 1000000, 1> mega;
typedef ratio< 1000000000, 1> giga;
typedef ratio< 1000000000000, 1> tera;
typedef ratio< 1000000000000000, 1> peta;
typedef ratio< 1000000000000000000, 1> exa;
typedef ratio< 1000000000000000000000, 1> zetta; //see below
typedef ratio<1000000000000000000000000, 1> yotta; //see below

ratio

Simple conversion of SI units

template<class From, class To, class T>
T convert(T from)
{
  typedef ratio_divide<From, To> result;
  return from * result::num / result::den;
}
typedef ratio<1,1> base;
typedef ratio<1760 * 3600, 3937> mile;

cout << "2.0501 km as " << convert<kilo, base>(2.0501) << " m"  << endl;
// 2.0501 km as 2050.1 m
cout << "  25.0 mm as " << convert<milli, base>(25.0) <<  " m"  << endl;
//   25.0 mm as 0.025m
cout << "  25.0 mm as " << convert<milli, centi>(25.0) << " cm" << endl;
//   25.0 mm as 2.5cm
cout << "   350 cm as " << convert<centi, kilo>(350.0) << " km" << endl;
// 350 cm as 0.0035 km
cout << "   2 mile as " << convert<mile, kilo>(2.) << " km" << endl;
// 2 mile as 3.21869 km

Time utilities

chrono

time_point is a template class specific for a clock.

duration is a template class specific for representation and a period.

typedef duration<integer_type, ratio<60>> minutes;
typedef duration<integer_type, milli> milliseconds;

// milli as typedef ratio<1, 1000> milli;

chrono

Example:

// get the current time :
chrono::system_clock::time_point now = chrono::system_clock::now();
chrono::system_clock::time_point later  = now + chrono::hours(1); // in one hour

auto diff1 = later - now; // in chrono::system_clock::duration
chrono::minutes diff2 = chrono::duration_cast<chrono::minutes>(later-now);

cout << "difference in min: " << diff2.count() << endl;
// difference in min : 60

typedef chrono::duration<double, ratio<3600,1>> dhours;

auto diff3 = chrono::duration_cast<dhours>(later-chrono::system_clock::now());
cout << "difference in hours: " << diff3.count() << endl;
// difference in hours : 0.999999

tuple

generalization of std::pair

    tuple<int, double, string> var(1, 3.14, "Hallo");

    int i = get<0>(var);
    double d = get<1>(var);
    get<2>(var) = "good bye";

Member access with compile time generated assessors get<N>();
(No iteration over elements at runtime.)

make_tuple & tie

make_tuple

Prefer the usage of make_tuple as you should prefer make_pair (without explicit template arguments).

tuple<int, double, string> foo()
{
    return make_tuple(1, 3.14, "C++");
}

tie

Assign multiple variables to a tuple (or pair).

    double d;
    int i;
    string s;
    tie(i, d, s) = foo();

    assert(i == 1 && d == 3.14 && s = "C++");

The combination of tuple and tie add multiple result values to C++.

Deprecated

STL:

Questions

/

#