The foundation of the Matrix Template Library is generic programming, which means that the user of MTL should have some familiarity with the concepts associated with generic programming, and especially with the STL. Much of the terminology used in these documents originates from the STL. The following web resources provide a good place to start learning about generic programming and the STL. Below we also give a very short description of generic programming.
The main facilitator in the separation of algorithms and containers in STL is the Iterator (sometimes called a ``generalized pointer''). Iterators provide a mechanism for traversing containers and accessing their elements. The interface between an algorithm and a container is specified by the types of iterators exported by the container. Generic algorithms are written solely in terms of iterators and never rely upon specifics of a particular container. Iterators are classified into broad categories, some of which are: InputIterator, ForwardIterator, and RandomAccessIterator.
The STL defines a set of requirements for each class of iterators. The requirements are in the form of which operations (functions) are defined for each iterator, and what the meaning of the operation is. Take a moment to examine the requirements (Valid Expressions) for RandomAccessIterator or any of the other iterator concepts.
For a concrete example of generic programming we will look at the algorithm accumulate(), which successively applies a binary operator to each element of a container. A typical use of accumulate() would be to sum the elements of a container, using the addition operator. The code below shows how one could implement the accumulate() algorithm in C++. The first and last arguments are iterators that mark the beginning and the end of the container. All of the arguments to the function have template types, so that the algorithm can be used with any container that exports the InputIterator interface.
templateThe syntax for iterator traversal is the same as for pointers, specifically operator++() increments to the next position. There are several other ways to move iterators (especially random access iterators). To access the container element pointed to by the iterator, one uses the dereference operator*(). One can also use the subscript operator[]() to access at an offset from the iterator. The code below is an example of how one could use the accumulate() template function with a vector and with a linked list (both from the STL).T accumulate(InputIterator first, InputIterator last, T init, BinaryOperator binary_op) { for (; first != last; ++first) init = binary_op(init, *first); return init; }
// using accumulate with a vector std::vectorEach STL container provides its own iterators that fulfill the standard interface requirements, but that are implemented in terms of the concrete data structure. In C++ this is facilitated by the ability to nest type definitions within classes. Consider the example below of a vector and list class. The Vector class implements its iterator by just using a pointer. The List's iterator is implemented in terms of the node class. Note that in each class, the iterator type has the same name, i.e., iterator. The common naming scheme gives a uniform way to access the iterator types within a container.x(10,1.0); double sum1 = accumulate(x.begin(),x.end(),0.0,plus ()); // using accumulate with a linked list std::list y; // copy vector's values into the list std::copy(x.begin(), x.end(), std::back_inserter(y)); double sum2 = accumulate(y.begin(),y.end(),0.0,plus ()); assert(sum1 == sum2); // they should be equal
templateWhen dealing with some container class, we can access the correct type of iterator using the double-colon scope operator, as is demonstrated above in the function foo().class Vector { typedef T value_type; typedef T* iterator; // Vector::iterator is a pointer ... }; template class List { typedef T value_type; struct node { T data; node* next; }; class iterator { // List::iterator is a nested class node* curr; iterator& operator++() { curr = curr->next; return *this; } }; ... }; template void foo(ContainerX& x, ContainerY& y) { typename ContainerX::iterator xi; typename ContainerY::iterator yi; ... }