Advanced C++

Standard Template Library

Máté Cserép

October 2015, Budapest

History

Alexander Stepanov:

  • Ada Generics (1979)
  • Designer of the C++ Standard Template Library
  • Which later became part of the C++ Standard Library

 

Standard Template Library

Part of the C++ standard. The most important example for generic programming. Generic programming: make more abstract routines without loosing efficiency using parameterization (both data and algorithm).

// Find the first occurrence of a value
int t[] = { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 };

int *p = find(t, t + sizeof(t) / sizeof(t[0]), 11);
if(p)
{
  p = 12;
}
// Specific implementation for arrays
int *find(int *begin, int *end, int v)
{
  while (begin != end )
  {
    if (*begin == v)
    {
      return begin;
    }
    ++begin;
  }
  return 0;
}

Generalization

Motivation

Generalization

template <typename T>
T *find(T *begin, T *end, const T& v)
{
  while (begin != end)
  {
    if (*begin == v)
    {
      return begin;
    }
    ++begin;
  }
  return 0;
}
template <typename It, typename T>
It find( It begin, It end, const T& v)
{
  while (begin != end)
  {
    if (*begin == v)
    {
      return begin;
    }
    ++begin;
  }
  return end;
}

Generalization on type int -> T

Generalization on data structure

Generalization

int t[] = { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 };

int *p = find(t, t + sizeof(t) / sizeof(t[0]), 11);
if(p)
{
  p = 12;
}

vector<int> v;
v.push_back(1);  v.push_back(3);  v.push_back(5);  v.push_back(7);  v.push_back(9);
v.push_back(11); v.push_back(13); v.push_back(15); v.push_back(17); v.push_back(19);

vector<int>::iterator vi = find(v.begin(), v.end(), 11);
if (vi != v.end())
{
  *vi = 12;
}

list<double> l;
l.push_back(1.1);   l.push_back(3.3);   l.push_back(5.5);   l.push_back(7.7);   l.push_back(9.9);
l.push_back(11.11); l.push_back(13.13); l.push_back(15.15); l.push_back(17.17); l.push_back(19.19);

list<double>::iterator li = find(l.begin(), l.end(), 11.11);
if (li != l.end())
{
  *li = 12.12;
}

Uniform usage

Generalization

const int t[] = { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 };

const int *pi = find(t, t + sizeof(t) / sizeof(t[0]), 11);
if (pi)
{
  //*pi = 12;      // Syntax error
  cout << *pi;
}

vector<int> v;
v.push_back(1.1);   v.push_back(3.3);   v.push_back(5.5);   v.push_back(7.7);   v.push_back(9.9);
v.push_back(11.11); v.push_back(13.13); v.push_back(15.15); v.push_back(17.17); v.push_back(19.19);

const list<double> cl(v.begin(), v.end());

// const_iterator != const iterator
list<double>::const_iterator cli = find(cl.begin(), cl.end(), 11.11);
if (cli != cl.end())
{
  //*cli = 12.12;  // Syntax error
  cout << *cli;
}

Constant safety

Further generalization

// Find the 3rd occurrence:
list<double> l;
l.push_back(1.1);   l.push_back(3.3);
l.push_back(5.5);   l.push_back(7.7);
l.push_back(9.9);   l.push_back(11.11);
l.push_back(13.13); l.push_back(15.15);
l.push_back(17.17); l.push_back(19.19);

list<double>::iterator li;
li = find(l.begin(), l.end(), 3.14);
li = find(li, l.end(), 3.14);
li = find(li, l.end(), 3.14);

// or: 
li = find(find(find(l.begin(), l.end(), 3.14), 
               l.end(), 3.14), 
          l.end(), 3.14);
template <typename It, typename Pred>
It find_if(It begin, It end, Pred p)
{
  while (begin != end)
  {
    if ( p(*begin) )
    {
        return begin;
    }
    ++begin;
  }
  return end;
}

Generalization

// Pred1: not too good
bool less55_3rd( int x)
{
  static int cnt = 0;
  if ( x < 55 )
    ++cnt;
  return 3 == cnt;
}
vector<int> v;
v.push_back(1);  v.push_back(3);
v.push_back(5);  v.push_back(7);
v.push_back(9);  v.push_back(11);
v.push_back(13); v.push_back(15);
v.push_back(17); v.push_back(19);

vector<int>::iterator = 
    find_if(v.begin(), v.end(), less55_3rd);
vector<int>::iterator = 
    find_if(v.begin(), v.end(), less55_3rd);

Predicates

Generalization

// Pred2: functor instead of function
struct less55_3rd
{
  less55_3rd() : cnt(0) { }
  bool operator()(int x)
  {
    if ( x < 55 )
      ++cnt;
    return 3 == cnt;
  }
private:
  int cnt;
};
vector<int> v;
v.push_back(1);  v.push_back(3);
v.push_back(5);  v.push_back(7);
v.push_back(9);  v.push_back(11);
v.push_back(13); v.push_back(15);
v.push_back(17); v.push_back(19);

less55_3rd pp;
vector<int>::iterator = 
    find_if(v.begin(), v.end(), pp);

// or:
vector<int>::iterator =
    find_if(v.begin(), v.end(), less55_3rd());

Predicates

Generalization

// Pred3: more generic
template <typename T>
struct less_nth
{
  less_nth(const T& t, int n) 
    : _t(t), _n(n), _cnt(0) { }
  
  bool operator()(const T& t)
  {
    if (t < _t)
      ++cnt;
    return _n == cnt;
  }

private:
  T   _t;
  int _n;
  int _cnt;
};
vector<int> v;
v.push_back(1);  v.push_back(3);
v.push_back(5);  v.push_back(7);
v.push_back(9);  v.push_back(11);
v.push_back(13); v.push_back(15);
v.push_back(17); v.push_back(19);

vector<int>::iterator = 
  find_if(v.begin(), v.end(), 
          less_nth<int>(55,3));

Predicates

Standard Template Library

Part of the C++ standard. The most important example for generic programming. Generic programming: make more abstract routines without loosing efficiency using parameterization (both data and algorithm).

Components:

  • Containers
  • Iterators
  • Allocators
  • Algorithms
  • Functors, binders, etc.

Iterators

  • all: can be copied and incremented
  • input: can be dereferenced as an rvalue, equality comparison
  • output: can be dereferenced as an lvalue
  • forward: input + output + default constructible
  • bidirectional: forward + can be decremented
  • random access: bidirectional + arithmetic operators, 
                                 inequality comparisons 
template<class InputIt, class T>
InputIt find(InputIt first, InputIt last, const T& value)
{
    for (; first != last; ++first) {
        if (*first == value) {
            return first;
        }
    }
    return last;
}

Categories

Iterators

  • iterator (vanilla)
  • constant iterator
  • iterator adaptors
    • reverse iterator
    • insert iterators
  • stream iterators

Types

Containers

  1. Sequential containers
    • vector, deque, list
  2. Adapter containers
    • stack, queue, priority_queue
  3. Associative containers
    • ordered associative containers
      • set, multiset, map, multimap
    • unordered associative containers (C++11)
      • undered_set, unordered_multiset,
        unordered_map, unordered_multimap

Adapter containers

stack

Op: empty, size, back (->top), push_back (->push), pop_back (->pop)
Cont: deque (default), vector, list


queue

Op: empty, size, front, back, push_back (->push), pop_front (->pop)
Cont: deque (default), list


priority_queue

Op: empty, size, front, push_back (->push), pop_back (->pop)
Cont: vector (default), deque

Ordered associative containers

  • Introduced in C++98.
  • Elements must be stored in an ordered data structure.
    (Typically red-black trees are used.)
  • Asymptotic complexity of all operations are logarithmic compared to the size of the container. 

Unordered associative containers

  • Introduced in C++11.
  • Elements must be stored based on a hash evaluation, which practically means hash-table.
  • Asymptotic complexity of all operations are constant in an average case and linear in the worst case compared to the size of the container.

Beside these, they have a pretty much common interface.

Container comparison

vector:

  •     dynamic array, random access,
  •     iterator invalidates: when realloc

deque:

  • array of arrays, random access
  • iterator invalidates:
    • insertion, and deletion other than front() and back()
    • deleting front() or back() invalidates only iterators referring them
    • it is possible that iterators are invalidated but pointers not

list:

  • bidirectional linked list, linear access
  • iterator invalidates: only referring to deleted items

ordered associative:

  • (typically) red-black tree, logarithmic access
  • iterator invalidates: only referring to deleted items

unordered associative:

  • hash tables, constant access
  • iterator invalidates: only referring to deleted items

Container comparison

vector deque list
typical internal data structure dynamic array array of arrays doubly linked list
elements value value value
duplicates allowed yes yes yes
random access yes yes no
iterator category random random bidirectional
search/find element slow slow very slow
fast insert/remove at end at front & at end anywhere
inserting/removing invalidates iterator, ptr, ref on reallocation always never
frees memory for removed elements never sometimes always
allows memory reservation yes no ---
transaction safe
exception safe
push_back()
pop_back()
push_back()
​pop_back()
push_front()
pop_front()
all
except sort()

Sequential

Container comparison

set multiset map multimap
typical internal data structure binary tree binary tree binary tree binary tree
elements value value key/value key/value
duplicates allowed no yes no yes
random access no no with key no
iterator category bidirectional bidirectional bidirectional bidirectional
search/find element fast fast fast (key) fast (key)
fast insert/remove --- --- --- ---
inserting/removing invalidates iterator, ptr, ref never never never never
frees memory for removed elements always always always always
allows memory reservation --- --- --- ---
transaction safe
exception safe
all
except multiple element insert()
all
​except multiple element insert()
all
​except multiple element insert()
all
​except multiple element insert()

Ordered associative

Container comparison

unordered_set unordered_multiset unordered_map unordered_multimap
typical internal data structure hash table hash table hash table hash table
elements value value key/value key/value
duplicates allowed no yes no yes
random access no no with key no
iterator category forward forward forward forward
search/find element fast fast fast (key) fast (key)
fast insert/remove --- --- --- ---
inserting/removing invalidates iterator, ptr, ref never never never never
frees memory for removed elements never never never never
allows memory reservation yes yes yes yes
transaction safe
exception safe
all
except multiple element insert()
all
​except multiple element insert()
all
​except multiple element insert()
all
​except multiple element insert()

Unordered associative

Advanced C++: Standard Template Library

By Cserép Máté

Advanced C++: Standard Template Library

  • 154