The Standard Template Library



What are Templates?

Generic "blueprints" which can be used to create instances of Abstract Data Types for different data types without having to rewrite code. There are two types of templates:
  1. Function Templates
  2. Class Templates

Function Templates

These are templates which can be implemented to handle any type of data. You define the template function with generic arguments and then when you make a call to the function in your code. The compiler uses the template and the data types of the arguments to generate a function which uses the appropriate data type.

Example: The max function
#include 
using namespace std ;

//-------------------------------------------------------------
//max returns the maximum of the two elements
//-------------------------------------------------------------
template <class T>
T max(T a, T b)
{
	return a > b ? a : b ;
}

void main()
{
    cout << "max(10, 15) = " << max(10, 15) << endl ;
    cout << "max('k', 's') = " << max('k', 's') << endl ;
    cout << "max(10.1, 15.2) = " << max(10.1, 15.2) << endl ;
}
Example: Bubble Sort
/******************************************/
/* BubbleSort()                           */
/* Requires that the operators > and = are*/
/*    overloaded for class T.             */
/******************************************/
template <class T, int>
void BubbleSort(T DataArray[], int count)
{
    int    i, j;
    T      temp;

    for(i=0; i<count; i++)
        for(j=0; j<(count-i - 1); j++)
        {
            if(DataArray[j] > DataArray[j+1])
            {
                 temp = DataArray[j];
                 DataArray[j] = DataArray[j+1];
                 DataArray[j+1] = temp;
            }
        }
}

Class Templates

These allow you to create generic classes for any type of data.

Example: Stack template (not to be confused with the stack template in the STL)
//--------------------------------------------------------
// StackTemplate.h - A generic stack class
//--------------------------------------------------------
#ifndef STACKTEMPLATE_H
#define STACKTEMPLATE_H
template <class T>
class Stack
{
public:
    Stack(int = 10) ;// Default size = 10 
    ~Stack() { delete [] stackPtr ; }
    bool push(const T&); 
    bool pop(T&) ;  
    bool isEmpty()const { return top == -1 ; } 
    bool isFull() const { return top == size - 1 ; } 
private:
    int size ;  // number of elements on Stack.
    int top ;  
    T* stackPtr ;  
} ;

// Constructor with the default size 10
template <class T>
Stack<T>::Stack(int s)
{
    size = ((s > 0) && (s < 1000)) ? s : 10 ;  
    top = -1 ;  // initialize stack
    stackPtr = new T[size] ; 
}
// push an element onto the Stack 
template <class T>
bool Stack<T>::push(const T& item)
{
    if (!isFull())
    {
        stackPtr[++top] = item ;
        return 1 ;  // push successful
    }
    return 0 ;  // push unsuccessful
}

// pop an element off the Stack
template <class T> 
bool Stack<T>::pop(T& popValue) 
{
    if (!isEmpty())
    {
        popValue = stackPtr[top--] ;
        return 1 ;  // pop successful
    }
    return 0 ;  // pop unsuccessful
}

An example using the Stack template class
#include <iostream>
#include "Stack.h"
using namespace std ;
void main()
{
    typedef Stack<float> FloatStack ;
    typedef Stack<int> IntStack ;

    FloatStack fs(5) ;
    float f = 1.1 ;
    cout << "Pushing elements onto fs" << endl ;
    while (fs.push(f))
    {
        cout << f << ' ' ;
        f += 1.1 ;
    }
    cout << endl << "Stack Full." << endl
        << endl << "Popping elements from fs" << endl ;
    while (fs.pop(f))
        cout << f << ' ' ;
    cout << endl << "Stack Empty" << endl ;
    cout << endl ;

    IntStack is ;
    int i = 1.1 ;
    cout << "Pushing elements onto is" << endl ;
    while (is.push(i))
    {
        cout << i << ' ' ;
        i += 1 ;
    }
    cout << endl << "Stack Full" << endl
        << endl << "Popping elements from is" << endl ;
    while (is.pop(i))
        cout << i << ' ' ;
    cout << endl << "Stack Empty" << endl ;
}

The output from the above example

But be aware that that with class templates:
  1. Header and implementation must both be in the same file (the .h file). They cannot be in separate files (.h and .cpp).
  2. While the interface defining all the STL template classes is defined by ANSI the implementation is not. Thus, you may find slightly different implementations in different compilers. But, this should not matter since the interface is the same. Most compilers today use the original Stepanov and Lee 1994 implementation from Hewlett Packard.
Now, while you can create your own template classes you will mostly be using those already created in the Standard Template Library.

The Standard Template Library

Historic Notes


  • Primarily developed by Alexander Stepanov starting in 1979. He referred to it as Generic Programming
  • Ada was the first language to incorporate this idea.
  • Working at General Electric Research and Development with David Musser they first published their work in Ada in 1987.
  • Because Ada has not had much acceptance outside the Defense Industry they switched their work to C++. Also, because the C/C++ use of pointers made the flexibility of templates possible.
  • Stepanov continued his research, first at AT&T Bell Laboratories, and then at Hewlett Packard.
  • In 1992 Meng Lee became a major contributor.
  • This research may have continued for some time and resulted in an HP proprietary library had not Andrew Koenig of Bell labs gotten wind of it and asked Stepanov to present it to the ANSI/ISO committee for inclusion in the C++ standard.
  • The committee approved and Stepanov and Lee were able to get a first cut ready for the committee by 1994.
  • Their version of the STL was approved and in August of 1994 HP made it widely available by publishing it on the internet.

Advanced C++ that is the basis for the STL

The ability to create templates in C++ is based on three things that are part of the language.
  1. Void pointers
  2. Function overloading
  3. Pointers to functions

void pointers

With typed pointers, i.e. int *iptr he pointer can only point to an int variable. Void pointers, however, can be used to store any memory address, but do require type casting to be used:
	int x = 5;		// Create an int pointer
	void *vptr;		// Create a pointer with no type associated with it
	vptr = reinterpret_cast(&x);// Cast address of an int to a void pointer
	*(reinterpret_cast (vptr)) = 10; // Cast vptr to an int pointer to change x

Function overloading

Templates produce compile time polymorphism. That is, new code is generated by the compiler to handle different data types. "Standard" function overloading produces run-time polymorphism. That is, all versions of the overloaded function exist and be created by the compiler, but different action is obtained by calling the appropriate version. For example: If you had five different overloaded functions called Multiply all five would be compiled and included in your code even if you only called two of the functions. With a Multiply function template only the two versions used in the program would be created and compiled into the application.

Pointers to Functions

If you have a function: void myFunction(int x) you can create a pointer to a function of that type and set the pointer pointing to the function. The pointer can then be used to call the function:
	void (*funcPtr)(int);     // Declare a function pointer
	funcPtr = myFunction;     // Set the pointer to myFunction
	funcPtr(255);             // Call the function using the pointer 
The implication here is that with templates you can pass into a function a pointer to another function which it can then call. For example using the BubbleSort function given above:
	myClass array[50];
	void (*sort)(myClass[], int);
	sort = BubbleSort;
	sortMyClass(array, sort, 50);  // pass the sort function

	template <Class T>
	void sortMyClass (myClass *arr, int count, (void (*fp)(T [], int)))
	{
	    fp(arr, count); 
	}

What is in the STL

The Standard Template Library provides a number of important tools for programmers which fall into three categories.
  1. Containers
  2. Algorithms
  3. Iterators

Containers

As the name implies these are data structures that "contain" or hold things. There are three major types of containers:
  1. Ordered
  2. Unordered
  3. Other
Note: As of Fall, 2008 the following are not part of the C++ Standard Template Library but you can find them in SGIs' STL extensions and in the GNU C++ Library. They are scheduled to be added to the C++ standard as of Technical Report 1 (TR1). TR1 is also supposed to include some functionality related to regular expressions, smart pointers (an abstract data type that simulates a pointer while providing additional features such as automatic garbage collection or bounds checking), hash tables, and random number generators. Four new templates will be added to the Unordered group. All of these are similar to the template whose name is part of their name (set, multiset, map, and multimap) but they are implemented using a hash table. A hash function must exist for each key type.
  1. hash_set When released in TR1 will have the name of unordered_set
  2. hash_multiset When released in TR1 will have the name of unordered_multiset
  3. hash_map When released in TR1 will have the name of unordered_map
  4. hash_multimap When released in TR1 will have the name of unordered_multimap

Container Adapters

These allow you to modify one of the container templates for a specific use. Basically these wrap a container class and provide for specialized functionality. There are three container adapters:
  1. stack
  2. queue
  3. priority_queue

Algorithms

Algorithms are template functions that can be applied to containers to process their contents in various ways. For example: Sort, Compare, Merge, Insert, Delete, Swap, Copy, and Fill. These are represented by template functions which are not members of classes. They are standalone functions. The basic idea is that the same algorithm (for example sort) can be applied to any container object. This includes arrays and any container objects you create. All these are defined in headers <algorithm>, <functional>, and <numeric>.

Iterators

Think of an iterator as a generalized pointer that can point to items in a container. It allows you to move through all of the items in a container. Iterators provide the means for containers and algorithms to interact. For example, n iterator provides a means for the sort algorithm to access each item found in a container and thus perform the sorting. If you have an iterator you can advance it to the next item in the container by using the ++ operator. You can also access the item the iterator references by "dereferencing" the iterator with the * operator just as if it were a pointer. (Which may give you a hint that maybe an iterator really is a pointer in disquise.) Types of Iterators There are six types of iterators including two special types. The names of each make it clear how they work, except for the last two.
  1. Forward iterators - iterate from the beginning to the end of a container.
  2. Reverse iterators - iterate from the end to the beginning of a container.
  3. Bidirectional iterators - iterate in both directions through a container.
  4. Random-access iterators - can reference any item in a container without a linear progression.
  5. Input iterators – Can be set to reference a specific device such as a file or cin and used to input sequences of items into a container.
  6. Output iterators – Can be set to reference a file or cout to sequentially output the items from a container
All iterators and related functions are defined in the header files <iterator>, <memarg>, and <utility>

So, let’s jump right in and look at some of the most used templates

Ordered Collections

<vector>

Overview
  1. Allows random access to any of the members.
  2. Can be thought of as a dynamic array of objects, i.e. has the ability to automatically resize itself when inserting or deleting an object.
  3. Inserting and removing an element to/from the end of the vector takes constant time (O(1))
  4. Inserting and removing an element to/from the beginning or middle of the vector takes linear time (O(n)), i.e. the bigger the vector the longer it takes.
  5. There is a special implementation for type bool which optimizes for space by storing boolean values as bits.
Constructors
	vector<double>dVec;             // empty vector to hold doubles
	vector<int>iVec(5,0);           // int vector of 5 elements all initialized to 0
	vector<float>fVec(20);          // float vector of 20 elements
	vector<string>sVec(10, "abc");  // string vector of 10 elements all set to “abc”
	vector<simple>simVec();         // empty vector to hold struct simple
Overloaded operators
	[]
Functions
Code Sample
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

using namespace std;
int main(int argc, char **argv)
{
   vector<string> SS;

   cout << "Creating string vector and adding 3 strings.\n";
   SS.push_back("The number is 10");
   SS.push_back("The number is 20");
   SS.push_back("The number is 30");

   cout << "Loop by index:" << endl;

   unsigned int i;
   for(i=0; i < SS.size(); i++)
   {
      cout << SS[i] << endl;
   }

   cout << endl << "Loop by Forward Iterator:" << endl;

   for(vector<string>::const_iterator cii = SS.begin(); cii != SS.end(); cii++)
   {
      cout << *cii << endl;
   }
   // FYI: A const_iterator can be used to iterate through the entire vector but
   //      prevents modifying any item in the vector.

   cout << endl << "Loop by Reverse Iterator:" << endl;

   for(vector<string>::reverse_iterator rii = SS.rbegin(); rii != SS.rend(); rii++)
   {
      cout << *rii << endl;
   }

   cout << "\nInformation on the vector:\n";
   cout << "\tsize = " << SS.size() << endl;
   cout << "\tcapacity = " << SS.capacity() << endl;
   cout << "\tmax_size = " << SS.max_size() << endl;

   cout << "\tSS[0] = " << SS[0] << endl;
   cout << "\tSS[2] = " << SS[2] << endl << endl;

   // Demonstration of a template function
   cout << "Calling swap(SS[0], SS[2]);\n";
   swap(SS[0], SS[2]);
   cout << "\tSS[0] = " << SS[0] << endl;
   cout << "\tSS[2] = " << SS[2] << endl << endl;
	
   SS[0] = "Can I do this?";
   cout << "SS[0] = " << SS[0] << endl;
	return 0;
}

<list>

Overview
  1. Created as a double linked list.
  2. Elements are not stored contiguously in memory as they are in a vector.
  3. Opposite performance from a vector. Slow lookup and access (linear time), but once a position has been found, quick insertion and deletion (constant time).
Constructors
	list <double>dList;       // empty list to hold doubles
	list <char>cList ();      // empty list to hold characters
	list <simple>simList ();  // empty list to hold struct simple
Functions
Code Sample
#include <iostream>
#include <list>
#include <string>

using namespace std;
int main(int argc, char **argv)
{
    list <int> L;

    cout << "Demonstrating push_back, push_front, and insert in a list.\n\n";
    L.push_back(1);             // Insert a new element at the end
    L.push_front(3);            // Insert a new element at the beginning
    L.insert(++L.begin(), 2);   // Insert "2" before position of first argument
    L.push_front(4);			// Insert a new element at the beginning
    L.push_back(5);				// Insert a new element at the end
    L.push_back(6);				// Insert a new element at the end

    cout << "Elements in the list (in order):\n\t";
    for(list<int>::iterator i=L.begin(); i != L.end(); ++i) cout << *i << " ";
    cout << endl << endl;

    cout << "Information on the list:\n";
    cout << "\tsize = " << L.size() << endl;
    cout << "\tmax_size = " << L.max_size() << endl << endl;

    cout << "Sorting the list:\n";
    L.sort();
    cout << "Elements in the list (in order):\n\t";
    for(i=L.begin(); i != L.end(); ++i) cout << *i << " ";
    cout << endl << endl;

    cout << "Demonstrating pop_back, pop_front, and remove from a list.\n";
    L.pop_back();           // Remove element at the end
    L.pop_front();          // Remove element at the beginning
    L.remove(3);			// Remove element from middle
    cout << "Elements in the list (in order):\n\t";
    for(i=L.begin(); i != L.end(); ++i) cout << *i << " ";
    cout << endl << endl;

    cout << "Demonstrating erase() to remove all but two elements.\n";
    L.erase(++L.begin(), --L.end());
    cout << "Elements in the list:\n\t";
    for(i=L.begin(); i != L.end(); ++i) cout << *i << " ";
    cout << endl << endl;

    cout < "Demonstrating assign(5, 2) to put five 2s in the list.\n";
    L.assign(5, 2);
    cout << "Elements in the list:\n\t";
    for(i=L.begin(); i != L.end(); ++i) cout << *i << " ";
    cout << endl << endl;

    return 0;
}

<deque>

Overview
  1. Name means Double Ended QUEue
  2. Similar to a Vector but provides for fast (O(1) inserts or deletes at the beginning or end of the deque.
  3. Care must be taken with any iterators after inserting or deleting as they may no longer be valid.
  4. Can do random inserts and deletes but they are less efficient.
Constructors
	deque<double>dDeque;       // empty deque to hold doubles
	deque <char>cDeque ();     // empty deque to hold characters
	deque <simple>simDeque (); // empty deque to hold struct simple
Overloaded operators
	[]
Functions
Code Sample
#include <iostream>
#include <deque>
#include <algorithm>
#include <string>

using namespace std;
int main(int argc, char **argv)
{
   deque<string> DD;

   cout << "Creating string deque and adding 3 strings using push_front.\n";
   DD.push_front("Deque the halls 1");
   DD.push_front("Deque the halls 2");
   DD.push_front("Deque the halls 3");

   cout << "Creating string deque and adding 3 strings using push_back.\n\n";
   DD.push_back("Deque the halls 4");
   DD.push_back("Deque the halls 5");
   DD.push_back("Deque the halls 6");

   cout << "Loop by index:" << endl;

   unsigned int i;
   for(i=0; i < DD.size(); i++)
   {
      cout << DD[i] << endl;
   }

   cout << endl << "Constant Iterator:" << endl;

   for(deque<string>::const_iterator=DD.begin(); cii!=DD.end(); cii++)
   {
      cout << *cii << endl;
   }
   // FYI: A const_iterator can be used to iterate through the entire deque but
   //      prevents modifying any item in the deque.

   cout << endl << "Reverse Iterator:" << endl;

   for(deque<string>::reverse_iterator rii=DD.rbegin(); rii!=DD.rend(); rii++)
   {
      cout << *rii << endl;
   }

   cout << "Information on the vector:\n";
   cout << "\tsize = " << DD.size() << endl;
   cout << "\tmax_size = " << DD.max_size() << endl;
   cout << "\tDD[0] = " << DD[0] << endl;
   cout << "\tDD[2] = " << DD[2] << endl << endl;
   cout << "Calling swap(DD[0], DD[2]);\n";
   swap(DD[0], DD[2]);
   cout << "\tDD[0] = " << DD[0] << endl;
   cout << "\tDD[2] = " << DD[2] << endl << endl;
	
   return 0;
}
Do you begin to see some commonality among these templates? There is an attempt to create a common interface within the limits of what each is supposed to do, e.g. list does not use the [ ] operator because a list does not allow random access.

Unordered Collections

<set> and <multiset>

Overview
  1. Even though sets are usually considered an unsorted collection the set template is implemented as a sorted collection of elements.
  2. Inserting/erasing elements in a set does not invalidate iterators pointing in the set.
  3. Provides set operations of union, intersection, difference, and symmetric difference ( sym dif = A XOR B, i.e. set of all elements not in both sets, inverse of the intersection).
  4. Also provides for a test of inclusion.
  5. Any data types used for a set must implement the comparison operator < or a custom comparator function must be specified.
  6. Actually implemented internally as a self-balancing binary search tree. (See example of an AVL tree in the Code Vault.)
  7. Set allows only 1 instance of a particular element, multiset allows duplicate elements.
Constructors
	set<char> cSet;                      // Set of characters
	int iArr[] = {5, 10, 25, 50, 100};
	set<int> iSet(iArr, iArr+5);         // Set of ints built from iArr
	                                        // using pointers to the first and last
	                                        // elements as iterators.
	bool simpleLessThan(simple *s1, simple *s2)
	{
	    return s1->X < s2->X;
	}
	// Set of simple with a given function to do "less than"
	set<simple, bool(*)(simple *, simple *)> sSet(simpleLessThan); 
Overloaded operators
	Must overload < or provide a "less than" comparison function 
Functions
Code Sample
#include <iostream>
#include <set>
#include <algorithm>
#include <string>

using namespace std;

// Simple structure
struct simple
{
	int X;
	char ch;
};

// Comparator function for set demo using X field of a struct simple
bool simpleLessThan(const simple &s1, const simple &s2) 
{
	return s1.X < s2.X;
}

int main(int argc, char **argv)
{
	// Create an empty set of characters
	set<char> cSet1;		
	set<char> cSet2;
	set<char> cSet3;

	cout << "Creating char set 1 and adding 10 characters.\n";
	for(char c = 'A'; c < 'K'; c++)
	{
		cSet1.insert(c);
	}
	cout << "Printing contents of char set 1 using iterators begin and end.\n\t";
	for(set<char>::iterator itr = cSet1.begin(); itr != cSet1.end(); itr++)
	{
		cout << *itr << " ";
	}
	cout << "\nInformation on cSet1:\n";
	cout << "\tsize = " << cSet1.size() << endl;
	cout << "\tmax_size = " << cSet1.max_size() << endl;
	cout << "\tcount = " << cSet1.count('A') << endl;
	cout << "Demonstrating find function on cSet1\n";
	char ch = 'C';
	set<char>::const_iterator cItr;
     // FYI: A const_iterator can be used to iterate through the entire set but
     //      prevents modifying any item in the set.
	cItr = cSet1.find(ch);
	cout << "\tfind('C') returned iterator to " << *cItr;
	cout << "\n\n";

	cout << "Creating char set 2 and adding 10 characters.\n";
	for(char c = 'E'; c < 'N'; c++)
	{
		cSet2.insert(c);
	}
	cout << "Printing contents of char set 2 using iterators begin and end.\n\t";
	for(set<char>::iterator itr = cSet2.begin(); itr != cSet2.end(); itr++)
	{
		cout << *itr << " ";
	}

	cout << "\n\nCreating char set 3 as a unnion of cSet1 and cSet2.\n";
	insert_iterator<set<char>>unionItr(cSet3, cSet3.begin()); // iterator for result
	set_union(cSet1.begin(), cSet1.end(), cSet2.begin(), cSet2.end(), unionItr);
	cout << "Printing contents of char set 3 using iterators begin and end.\n\t";
	for(set<char>::iterator itr = cSet3.begin(); itr != cSet3.end(); itr++)
	{
		cout << *itr << " ";
	}

	cout << "\n\nClearing set 3\n";
	cout << "Refilling set 3 as an intersection of cSet1 and cSet2.\n";
	cSet3.clear();
	insert_iterator<set<char>>intersectionItr(cSet3, cSet3.begin()); // iterator for result
	set_intersection(cSet1.begin(), cSet1.end(), cSet2.begin(), cSet2.end(), intersectionItr);
	cout << "Printing contents of char set 3 using iterators begin and end.\n\t";
	for(set<char>::iterator itr = cSet3.begin(); itr != cSet3.end(); itr++)
	{
		cout << *itr << " ";
	}
	cout << "\n\n";

	cout << "\n\nClearing set 3\n";
	cout << "Refilling set 3 as the difference of cSet1 and cSet2.\n";
	cSet3.clear();
	insert_iterator<set<char>>diffItr(cSet3, cSet3.begin()); // iterator for result
	set_difference(cSet1.begin(), cSet1.end(), cSet2.begin(), cSet2.end(), diffItr);
	cout << "Printing contents of char set 3 using iterators begin and end.\n\t";
	for(set<char>::iterator itr = cSet3.begin(); itr != cSet3.end(); itr++)
	{
		cout << *itr << " ";
	}
	cout << "\n\n";

	cout << "\n\nClearing set 3\n";
	cout << "Refilling set 3 as the symmetric difference of cSet1 and cSet2.\n";
	cSet3.clear();
	insert_iterator<set<char>>symDiffItr(cSet3, cSet3.begin()); // iterator for result
	set_symmetric_difference(cSet1.begin(), cSet1.end(), cSet2.begin(), cSet2.end(), symDiffItr);
	cout << "Printing contents of char set 3 using iterators begin and end.\n\t";
	for(set<char>::iterator itr = cSet3.begin(); itr != cSet3.end(); itr++)
	{
		cout << *itr << " ";
	}
	cout << "\n\n";

	// Create a set of 5 ints from an array of ints by giving the
	//   iterators for begin and end
	int iArr[] = {5, 10, 25, 50, 100};
	set<int> iSet(iArr, iArr+5);				

	// Create a set of struct simple with a required function to do "less than" comparison
	set<simple, bool(*)(const simple &, const simple &)> sSet(simpleLessThan); 


	cout << "Creating int set and initializing to {5, 10, 25, 50, 100}.\n";
	cout << "Printing contents of int set using iterators begin and end.\n\t";
	for(set<int>::iterator itr = iSet.begin(); itr != iSet.end(); itr++)
	{
		cout << *itr << " ";
	}
	cout << "\n\n";

	cout << "Creating struct simple set and adding 5 elements.\n";
	for(int i=0; i<5; i++)
	{
		simple *s1 = new simple();
		s1->X = i;
		s1->ch = 'A' + i;
		cout << "\tAdding " << s1->X << ", " << s1->ch << " to the set.\n";
		sSet.insert(*s1);
	}
	cout << "Getting iterators to two elements to demonstrate < operator.\n";
	set<simple, bool(*)(const simple &, const simple &)>::iterator simItr1 = sSet.begin();

	set<simple, bool(*)(const simple &, const simple &)>::iterator simItr2 = sSet.begin();
	simItr2++;
	simItr2++;
	cout << "Comparing simItr1 = " << simItr1->X << ", " << simItr1->ch << endl;
	cout << "    with  simItr2 = " << simItr2->X << ", " << simItr2->ch << endl;
	if(simpleLessThan(*simItr1, *simItr2))
		cout << "simItr1 is less than simItr2" << endl;
	else
		cout << "simItr1 is greater than simItr2" << endl;
	cout << "Printing contents of struct set using iterators begin and end.\n";
	for(simItr1 = sSet.begin(); simItr1 != sSet.end(); simItr1++)
	{
		cout << "\tStruct = " << simItr1->X << ", " << simItr1->ch << endl;
	}

	return 0;
}

<map> and <multimap>

Overview
  1. A sorted associative array, i.e. it allows mapping from one data item (a key) to another (a value). Similar to the use of a key field in a class used to build a sorted linked list.
  2. Given a key you can find the associated value.
  3. Type of key must implement comparison operator < or custom comparator function must be specified.
  4. Also implemented internally as a self-balancing binary search tree.
  5. Map allows only 1 instance of a particular key. Multimap allows duplicate keys and allows a single key to map to multiple items.
Constructors
	map<int, string> Employees;  // Create map of strings keyed on int
	multimap<string, int> m;     // Create multimap of ints keyed on string
Overloaded operators
	Must overload < or provide a less than comparison function 
	[ ] – overloaded for insertion
Functions
Code Sample
#include <iostream>
#include <map>
#include <algorithm>
#include <string>
#include <utility>

using namespace std;

int main()
{
   map<int, string> Employees;

   //---------------------------------------------------------------------
   //                      Demonstrate map template
   //---------------------------------------------------------------------
   // Assignment using array index notation
   cout << "Creating map<int, string> Employees and adding 5 members\n";
   Employees[5234] = "Mike C.";
   Employees[3374] = "Charlie M.";
   Employees[1923] = "David D.";
   Employees[7582] = "John A.";
   Employees[5328] = "Peter Q.";

   // Print using iterator
   cout << "Printing all members using iterators through the pair objects\n";
   for( map<int,string>::iterator ii=Employees.begin(); ii!=Employees.end(); ++ii)
   {
       cout << "\t" <<(*ii).first << ": " << (*ii).second << endl;
   }

   cout << "\nPrinting all members using their index notation\n";
   cout << "\tEmployees[5234]=" << Employees[5234] << endl;
   cout << "\tEmployees[3374]=" << Employees[3374] << endl;
   cout << "\tEmployees[1923]=" << Employees[1923] << endl;
   cout << "\tEmployees[7582]=" << Employees[7582] << endl;
   cout << "\tEmployees[5328]=" << Employees[5328] << endl << endl;

   cout << "Using find to locate an element with Employees.find(3374)\n";
   cout << "  Note: find returns a pair object.\n";
   map<int, string>::iterator itr = Employees.find(3374);
   cout << "\tFound:  [" << (*itr).first << ", " << (*itr).second << "]" << endl << endl;

   cout << "Information on the map:\n";
   cout << "\tsize = " << Employees.size() << endl;
   cout << "\tmax_size = " << Employees.max_size() << endl << endl;

   //---------------------------------------------------------------------
   //                      Demonstrate map template
   //---------------------------------------------------------------------
   // Compare (<) function not required since it is built into string class.
   // else declaration of a comparison function would have to be included
   // in the multimap definition i.e. multimap<string, int, compare> m;
   
   cout << "Creating multimap<string, int> m and adding 6 members\n";

   multimap<string, int> m;

   m.insert(pair<string, int>("a", 1));
   m.insert(pair<string, int>("c", 2));
   m.insert(pair<string, int>("b", 3));
   m.insert(pair<string, int>("b", 4));
   m.insert(pair<string, int>("a", 5));
   m.insert(pair<string, int>("b", 6));

   cout << "Number of elements with key a: " << m.count("a") << endl;
   cout << "Number of elements with key b: " << m.count("b") << endl;
   cout << "Number of elements with key c: " << m.count("c") << endl;

  cout << "Elements in m: " << endl;
  for (multimap<string, int>::iterator it = m.begin(); it != m.end(); it++)
   {
       cout << "\t  [" << (*it).first << ", " << (*it).second << "]" << endl;
   }

   pair<multimap<string, int>::iterator, multimap<string, int>::iterator> ppp;

   // equal_range(b) returns pair<iterator,iterator> representing the range
   // of element with key b
   ppp = m.equal_range("b");

   // Loop through range of maps of key "b"
   cout << endl << "Range of \"b\" elements:" << endl;
   for (multimap<string, int>::iterator it2 = ppp.first; it2 != ppp.second; it2++)
   {
       cout << "  [" << (*it2).first << ", " << (*it2).second << "]" << endl;
   }
}

Other Unordered Containers

Be aware that these exist but because of time and space limitations and the fact that they are not used as much as the other containers we will not spend time on them.
  1. bitset
  2. valarray

Container Adapters

These allow you to modify one of the above mentioned sequence containers for a specific use. Basically these wrap a container class and provide for specialized functionality. There are 3 types:

<stack>

Overview
  1. It’s a LIFO structure (Last In First Out). You can push an item on the top of the stack or you can pop an item from the top of the stock. No other access is allowed.
  2. The stack template wraps a list template object and provides the behavior of a stack.
Constructors
	stack<dataType>  myStack;
This will create a list template of dataType objects and then place the list inside of a stack so you get the push and pop actions required of a stack.
Functions
Code Sample
#include <iostream>
#include <stack>

using namespace std;

int main()
{
	stack<int> iStack;
	int val;

	// Push some stuff on the int stack
	cout << "Creating an int stack and pushing 10 values on it.\n\n";
	for(int i=0; i<10; i++)
	{
		val = i * 100;
		iStack.push(val);
	}
	cout << "Printing stack.top then popping values from the stack.\n\t ";
	while(!iStack.empty())
	{
		cout << iStack.top() << " ";
		iStack.pop();
	}
	if(iStack.empty())
		cout << "\n\nStack is now empty.\n";
	else
		cout << "\n\nOops! Why isn't the stack empty?\n"; // Won't happen
	cout << "\n\n";	
}

<queue>

Overview
  1. It’s a FIFO structure (First In First Out). You push items in at the tail of the queue and pop items off at the head of the queue.
  2. The queue template wraps a list template object and provides the behavior of a queue.
Constructors
	queue<dataType>  myQueue;
This will create a list template of dataType objects and then place the list inside of a queue so you get the push and pop actions of a queue.
Functions
Code Sample
#include <iostream>
#include <queue>
#include <list>

using namespace std;

int main()
{
	queue<char> cQueue;

	// Push some stuff on the char queue
	cout << "Creating a char queue and pushing 10 values on it.\n\n";
	for(char i='A'; i<'K'; i++)
	{
		cQueue.push(i);
	}
	cout << "\tQueue.front = " << cQueue.front() << endl;
	cout << "\tQueue.back = " << cQueue.back() << endl << endl;

	cout << "Printing queue.front then popping values from the queue.\n\t ";
	while(!cQueue.empty())
	{
		cout << cQueue.front() << " ";
		cQueue.pop();
	}
	if(cQueue.empty())
		cout << "\n\nQueue is now empty.\n";
	else
		cout << "\n\nOops! Why isn't the queue empty?\n"; // Won't happen
	cout << "\n\n";
}

<priority_queue> found in <queue>

Overview
  1. Works just like the queue class except that when you pop (dequeue) an item it removes the item from the list with the highest priority (value).
Constructors
	priority_queue<dataType>  myPQueue;
This will create a list template of dataType objects and then place the list inside of a priority_queue so you get the push and pop actions of a queue.
Functions
Code Sample
#include <iostream>
#include <queue>
#include <list>

using namespace std;

int main()
{
	priority_queue<int>piQueue;
	int val;

	// Push some stuff on the int queue
	cout << "Creating an int priority_queue and pushing 10 values on it.\n";
	cout << "  Values are pushed from smallest to largest.\n\n";
	for(int i=0; i<10; i++)
	{
		val = i * 100;
		piQueue.push(val);
	}
	cout << "\tpiQueue.top = " << piQueue.top() << endl << endl;

	cout << "Printing queue.top then popping values from the queue.\n";
	cout << "   Items popped from largest to smallest.\n\n\t ";
	while(!piQueue.empty())
	{
		cout << piQueue.top() << " ";
		piQueue.pop();
	}
	if(piQueue.empty())
		cout << "\n\nQueue is now empty.\n";
	else
		cout << "\n\nOops! Why isn't the queue empty?\n"; // Won't happen
	cout << "\n\n";	
}

Algorithms

Space and time limitations prevent going into any detail about other algorithms other than those already demonstrated. All of the algorithms are found in <algorithm> <functional> and <numeric>. The list below, however, will give you some idea as to the variety of template functions available.

Iterators

Iterators are defined in <iterator>. The main uses of iterators have already been shown in the previous demonstrations.