STL Implementations


Standard Containers

Consider the implementation details of std::vector that would be required to support this program:
void test(void)
{
    // Make it easy to switch containers
  typedef std::vector<int> ContainerType;

  ContainerType cont1;
  ContainerType::iterator it1;

    // Fill vector: 0  1  2  3  4  5  6  7  8  9
  unsigned int i;
  for (i = 0; i < 10; i++)
    cont1.push_back(i);

    // Insert 99: 0  1  2  99  3  4  5  6  7  8  9
  it1 = cont1.begin() + 3;
  cont1.insert(it1, 99);
  print5(cont1);// From before

    // Print with iterator dereference
  for (it1 = cont1.begin(); it1 != cont1.end(); ++it1)
    std::cout << *it1 << "  ";
  std::cout << std::endl;

    // Print with operator[]
  it1 = cont1.begin();
  for (i = 0; i < cont1.size(); i++)
    std::cout << it1[i] << "  ";
  std::cout << std::endl;
}
Iterator Categories: (A hierarchy of capabilities)
struct input_iterator_tag {};
struct output_iterator_tag {};
struct forward_iterator_tag : public input_iterator_tag {};
struct bidirectional_iterator_tag : public forward_iterator_tag {};
struct random_access_iterator_tag : public bidirectional_iterator_tag {};
Iterator traits: (A class of types)
template <class Iterator> 
struct iterator_traits
{
  typedef typename Iterator::iterator_category iterator_category;
  typedef typename Iterator::value_type value_type;
  typedef typename Iterator::difference_type difference_type;
  typedef typename Iterator::pointer pointer;
  typedef typename Iterator::reference reference;
};
A base class for std::iterator:
template <
          class Category, 
          class T, 
          class Difference = std::ptrdiff_t, 
          class Pointer = T*, 
          class Reference = T&
         >
struct iterator
{
  typedef Category iterator_category; // category of iterator
  typedef T value_type;               // type of element 
  typedef Difference difference_type; // type of iterator difference
  typedef Pointer pointer;            // return type of operator->
  typedef Reference reference;        // return type of operator*
};
The built-in iterator classes:

template <class _Tp, class _Distance> struct input_iterator : 
  public iterator <input_iterator_tag, _Tp, _Distance, _Tp*, _Tp&> {};
  
struct output_iterator : public iterator <output_iterator_tag, void, void, void, void> {};

template <class _Tp, class _Distance> struct forward_iterator :
  public iterator<forward_iterator_tag, _Tp, _Distance, _Tp*, _Tp&> {};

template <class _Tp, class _Distance> struct bidirectional_iterator :
  public iterator<bidirectional_iterator_tag, _Tp, _Distance, _Tp*, _Tp&> {};

template <class _Tp, class _Distance> struct random_access_iterator :
  public iterator<random_access_iterator_tag, _Tp, _Distance, _Tp*, _Tp&> {};
An implementation of a generic algorithm (count):
template<class InIter, class T> inline
typename iterator_traits<InIter>::difference_type count(InIter first, InIter last, const T& val)
{
  typename iterator_traits<InIter>::difference_type result = 0;
  while (first != last)
  {
    if (*first == val)
      ++result;
    ++first;
  }
  return result;
}
Recall this "universal" swap function
template <typename T>
void Swap(T &a, T &b)
{
  T temp = a;
  a = b;
  b = temp;
}
We need to create a temporary to implement the swap correctly. We don't know what type of temporary to create, but the template parameter T will "do the right thing."

But what if the temporary type is unknown? (There isn't even a T parameter.) That's the reason for the typdefs in the classes.

Using value_type to create a temporary variable:

template <typename FIter>
void rotate_left(FIter first, FIter last)
{
    // Convenience
  typedef typename std::iterator_traits<FIter>::value_type value_type;
  if (first != last)
  {
    FIter next = first;
    ++next;
    value_type temp(*first); // save first value
    while (next != last)
    {
      *first = *next; // shift left
      ++first;
      ++next;
    }
    *first = temp; // copy temp (first) to last
  }
}
We don't want to create a temporary iterator (which is what the FITer template parameter is for), we want to create a temporary for the type that the iterator is "pointing" at.


Implementing an STL-Compatiblie Container

Container interface

Recall our IntArray class.

The "skeleton" for our vector class, svector (simple vector):

template <typename T>
class svector
{
  public:
    svector();    // default constructor
    ~svector();   // Destructor (we're dynamically allocating an array)
      // Other public members to be declared/defined
  private:
    unsigned int size_;      // The number of elements in the array
    T* array_;               // The dynamically allocated array
    unsigned int capacity_;  // The allocated size of the array
};
Adding the first typedef:

template <typename T>
class svector
{
  public:
      // Convenience
    typedef unsigned int size_type;
    svector();    // default constructor
    ~svector();   // Destructor (we're dynamically allocating an array)
      // Other public members to be declared/defined
  private:
    size_type size_;      // The number of elements in the array
    T* array_;            // The dynamically allocated array
    size_type capacity_;  // The allocated size of the array
};
Partial implementations of the svector class:
template <typename T>
svector<T>::svector() : size_(0), array_(0), capacity_(0)
{
}

template <typename T>
svector<T>::~svector()
{
  // Release resources
}
So right now, we can support this program (woohoo...)
void f1(void)
{
  svector<int> cont1;
}
What we want to do is at least be able to support the sample program above.

So, this means we'll need at least a push_back method:

template <typename T>
void svector<T>::push_back(const T& value)
{
  // Add 'value' to the end 
}
It turns out that we'll need about 8 methods implemented in svector and its corresponding iterator class, svector_iter, to support this simple operation.

The beginnings of an iterator class, svector_iter for use with our simple vector class:

template <typename T>
class svector_iter 
{
  public:
    typedef ptrdiff_t difference_type; // type of diff between iterators
    svector_iter(T*);  // conversion constructor

  private:
    T* elem_ptr_;  // A pointer to some element
};

There are two "heavyweight" operations with vectors (dynamic arrays). They are:

Essentially, all other operations are fairly trivial and require on average one or two lines of code to accomplish.

Clearly, push_back is a form of insertion, so we should implement the insertion logic first and leverage that for the push_back operation.

If you recall, the insert method takes an iterator to the insertion point and a value and returns an iterator to the inserted item:

Declaration in the svector class:

svector_iter<T> insert(svector_iter<T> it, const T& value);

Implementation pseudo-code:

template <typename T>
svector_iter<T> svector<T>::insert(svector_iter<T> it, const T& value)
{
  // If the array is full, we need to grow it to accommodate the new element

  // Move all elements to the right of the insertion point over one slot

  // Insert the element in the available position
    
  // Increment 'size_' private member

  // Return iterator to newly inserted element
}
At this point, we'll introduce a few more typedefs to make life easier:

// Forward declaration
template <typename T> class svector_iter;

template <typename T>
class svector
{
  public:
      // Typedefs for STL compatibility
    typedef T value_type;              // type in the container
    typedef T& reference;              // ref type in container
    typedef const T& const_reference;  // const ref type in container
    typedef svector_iter<T> iterator;  // iterator type for this container
    typedef ptrdiff_t difference_type; // type of diff between iterators
    typedef unsigned int size_type;    // type of sizes in container

      // Required constructors
    svector();                                   // default constructor
    svector(size_type n, const_reference value); // construct n copies of value 
    svector(const svector& rhs);                 // copy constructor

    void push_back(const_reference value);
    iterator insert(iterator it, const_reference value);

      // Destructor (we're dynamically allocating an array)
    ~svector();

      // Other members to be declared/defined
  private:
    size_type size_;      // The number of elements in the array
    T* array_;            // The dynamically allocated array
    size_type capacity_;  // The allocated size of the array
};

For convenience, we'll have a private grow method that creates a new array:
template <typename T>
void svector<T>::grow(void)
{
    // Double the capacity
  capacity_ = (capacity_) ? capacity_ * 2 : 1;

    // Create the new array 
  T* new_array = new T[capacity_];

    // Copy all elements from old array to new array
  for (size_type i = 0; i < size_; ++i)
    new_array[i] = array_[i];

    // The new size is still the same (didn't add/remove anything)
  size_type new_size = size_;

    // If we had elements, release them
  if (array_)
  {
    // call clear() eventually
    delete [] array_;
  }

    // Set members to new values
  array_ = new_array;
  size_ = new_size;
}
Given the grow method, we can start to flesh out the insert method a little better:
template <typename T>
typename svector<T>::iterator svector<T>::insert(iterator it, const_reference value)
{
    // If the array is full, we need to grow it to accommodate the new element
  if (size_ == capacity_)
    grow();

    // Move all elements to the right of the insertion point over one slot
  for (size_type i = size_ - 1; i >= INSERTION_POINT; --i)
    array_[i + 1] = array_[i];

    // Insert the element in the available position
  array_[index] = value;
    
    // Increment 'size_'
  size_++;

    // Return iterator to newly inserted element
  return ???
}
This leaves us with two questions:
  1. Where is the insertion point?
  2. How do we return an iterator to the new element?
To get the index of the element from the iterator (pointer):
  // Find index since iterator will be invalidated after we grow
difference_type index = it - START_OF_ARRAY
We need this method in our container class:

Declaration:

iterator begin(void);

Definition:

template <typename T>
typename svector<T>::iterator svector<T>::begin(void)
{
  // return iterator to beginning of internal array (array_)
}
Now, we can use this to find the index:
  // Find index since iterator will be invalidated after we grow
difference_type index = it - begin();
Update the moving of the elements as well:
  // Move all elements to the right of the insertion point over one slot
for (size_type i = size_ - 1; i >= index; --i)
  array_[i + 1] = array_[i];
We also need to return an iterator to the newly-inserted element:
  // Return iterator to newly inserted element
return iterator( ??? );
So, our complete insert method looks like this:
template <typename T>
typename svector<T>::iterator svector<T>::insert(iterator it, const_reference value)
{
    // Find index since iterator will be invalidated after we grow
  difference_type index = it - begin();

    // If the array is full, we need to grow it to accommodate the new element
  if (size_ == capacity_)
    grow();

    // Move all elements to the right of the insertion point over one slot
  for (size_type i = size_ - 1; i >= index; --i)
    array_[i + 1] = array_[i];

    // Insert the element in the available position
  array_[index] = value;
    
    // Increment 'size_'
  size_++;

    // Return iterator to newly inserted element
  return begin() + index;
}
In addition to the supporting methods we've looked at so far, there are a few more that are necessary. Specifically, what additional methods are needed to support the code below?
difference_type index = it - begin();
return begin() + index;
The methods are part of the iterator classes:
template <typename T>
typename svector_iter<T>::difference_type svector_iter<T>::operator-(const svector_iter& rhs)) const
{
  // return difference between two iterators
}

template <typename T>
svector_iter<T> svector_iter<T>::operator+(difference_type rhs) const
{
  // return new iterator 
}
Ok, so what happened with our supposedly simple push_back method?
template <typename T>
void svector<T>::push_back(const_reference value)
{
  // Add 'value' to the end 
}
All of that just to add an element to a container. Ok, now we can support this portion of the sample program above.

svector<int> cont1;

  // Fill vector: 0  1  2  3  4  5  6  7  8  9
unsigned int i;
for (i = 0; i < 10; i++)
  cont1.push_back(i);
And the methods required are:

In the svector class:

svector(); 
void push_back(const_reference value);
iterator insert(iterator it, const_reference value);
iterator begin(void));
iterator end(void));
~svector();
void grow(void); // private

In the svector_iter class:

svector_iter(T*);  // conversion constructor (creates an iterator from a pointer)
difference_type operator-(const svector_iter& rhs)) const;
svector_iter operator+(difference_type rhs)) const;


Now, to print the container:

template <typename T>
void print(const T& v)
{
  typename T::const_iterator iter;
  for (iter = v.begin(); iter != v.end(); ++iter)
    std::cout << *iter << "  ";
  std::cout << std::endl;
}
Details about the const_iterator.

This code requires at least:

  1. default constructor for the container's const iterator
  2. an assignment operator for the const iterator
  3. a begin() and end() method for the container (const versions)
  4. a "not equals" operator for the const iterator
  5. a pre-increment for the const iterator
  6. a dereference operator for the const iterator
We have item 2. We need 1, 3, 4, 5, and 6:

svector:

const_iterator begin(void) const;
const_iterator end(void) const;
const_svector_iter:

const_svector_iter();  
bool operator!=(const const_svector_iter& rhs) const;
const_svector_iter& operator++(void);
const T& operator*(void) const;
These are all trivial to implement:
template <typename T>
const_svector_iter<T>::const_svector_iter() : elem_ptr_(0)
{
}

template <typename T>
bool const_svector_iter<T>::operator!=(const const_svector_iter &rhs) const
{
  // return bool based on operator==
}

template <typename T>
const_svector_iter<T>& const_svector_iter<T>::operator++(void)
{
  // Increment pointer and return reference
}

template <typename T>
const T& const_svector_iter<T>::operator*(void) const
{
  // return reference to element being "pointed at"
}
Now, we can support the print function and this code:

template <typename T>
void print(const T& v)
{
  typename T::const_iterator iter;
  for (iter = v.begin(); iter != v.end(); ++iter)
    std::cout << *iter << "  ";
  std::cout << std::endl;
}

void f1(void)
{
  svector<int> cont1;

    // Fill vector: 0  1  2  3  4  5  6  7  8  9
  unsigned int i;
  for (i = 0; i < 10; i++)
    cont1.push_back(i);
  print(cont1);
}
By adding a default constructor for svector_iter, the code supports this now as well:

svector<int> cont1;
svector_iter<int> it1;

  // Fill vector: 0  1  2  3  4  5  6  7  8  9
unsigned int i;
for (i = 0; i < 10; i++)
  cont1.push_back(i);
print(cont1);

  // Insert 99: 0  1  2  99  3  4  5  6  7  8  9
it1 = cont1.begin() + 3;
cont1.insert(it1, 99);
print(cont1);
The last step is to support this code below, which requires operator[] for the iterator and the size() method for the container:
  // Print with operator[]
it1 = cont1.begin();
for (i = 0; i < cont1.size(); ++i)
  std::cout << it1[i] << "  ";
std::cout << std::endl;
Like most of the methods, these are trivial as well:
template <typename T>
T& svector_iter<T>::operator[](difference_type index)
{
  return *(elem_ptr_ + index);
}

template <typename T>
typename svector<T>::size_type svector<T>::size(void) const
{
  return size_;
}
The classes so far:
Partial interface
Partial implementations


Adding typedefs from std::iterator

Suppose we want to use a generic algorithm with our container:
svector<int> cont1;

  // Fill vector: 0  1  2  3  4  5  6  7  8  9
for (int i = 0; i < 10; i++)
  cont1.push_back(i);

  // Count the occurrences of the number 5
std::count(cont1.begin(), cont1.end(), 5);
We are met with 4 compiler errors:
"_iterator_base.h": E2404 Dependent type qualifier 'svector_iter' has no member type named 'iterator_category' in function f2() at line 94
"_iterator_base.h": E2404 Dependent type qualifier 'svector_iter' has no member type named 'value_type' in function f2() at line 95
"_iterator_base.h": E2404 Dependent type qualifier 'svector_iter' has no member type named 'pointer' in function f2() at line 97
"_iterator_base.h": E2404 Dependent type qualifier 'svector_iter' has no member type named 'reference' in function f2() at line 98
The generic algorithms are expecting a certain interface (specific typedefs) to be present in the iterator class. These types are defined in the iterator traits.

Modified classes for svector, svector_iter, and const_svector_iter including additional typedef conveniences. We've also derived from std::iterator.

Note that the use of the generic algorithm count will still not compile. We'll come back to this and see what other problems still exist.


Implementing with Nested Classes

Currently, our client code looks like this:
svector<int> cont1;
svector_iter<int> it1;
Our iterator class is a separate, independent class. However, both of these statements are logically equivalent:
svector_iter<int> it1;
svector<int>::iterator it1;
This is because we used a typedef inside of the svector class:
typedef svector_iter<T> iterator;
This gives us the illusion that there is an iterator class that is part of the container class.

If we make the svector_iter class part of the public section of the svector class, we would be able to use this syntax:

svector<int>::svector_iter it1;
and, with the obligatory typedef:
typedef svector_iter iterator;
We could then use our iterator class as before.
svector<int>::iterator it1;
At this point, it may seem that we haven't gained anything. To the client, there is no difference, but nesting classes can help us organize our code better.

To convert our existing svector class to use nested iterators, it's as easy as 1-2-3...4-5

  1. Copy the iterator interfaces into the svector class:
    template <typename T>
    class svector
    {
      private:
        template <typename T>
        class svector_iter : public std::iterator<std::random_access_iterator_tag, T>
        {
         ...
        }
    
        template <typename T>
        class const_svector_iter : public std::iterator<std::random_access_iterator_tag, T>
        {
         ...
        }
        
        // svector stuff ...
        
    };
    
  2. Remove the template tags on the iterator classes:
      private:
        class svector_iter : public std::iterator<std::random_access_iterator_tag, T>
        {
         ...
        }
    
        class const_svector_iter : public std::iterator<std::random_access_iterator_tag, T>
        {
         ...
        }
    
  3. Modify the typedefs in the svector class by removing the type parameter T

    Before:

    typedef svector_iter<T> iterator;
    typedef const_svector_iter<T> const_iterator;
    
    After:
    typedef svector_iter iterator;
    typedef const_svector_iter const_iterator;
    
    This is what a partial interface looks like:
    template <typename T>
    class svector
    {
      private:
          // non-const iterator
        class svector_iter  : public std::iterator<std::random_access_iterator_tag, T>
        {
          public:
              // Public types/methods of svector_iter ...
          private:
            T* elem_ptr_;  // A pointer to some element
        };
    
          // const iterator
        class const_svector_iter : public std::iterator<std::random_access_iterator_tag, T>
        {
          public:
            // Public types/methods of const_svector_iter ...
          private:
            const T* elem_ptr_;  // A pointer to some element
        };
    
      public:
          // svector members
          // Required typedefs
        typedef T value_type;
        typedef T& reference;
        typedef const T& const_reference;
        typedef svector_iter iterator;
        typedef const_svector_iter const_iterator;
        typedef ptrdiff_t difference_type;
        typedef unsigned int size_type;
    
        // Public methods of svector ...
      private:
        size_type size_;      // The number of elements in the array
        T* array_;            // The dynamically allocated array
        size_type capacity_;  // The allocated size of the array
    
        void grow(void);      // Used internally to grow array
    };
  4. If implementing outside of the class, modify the implementation to preprend svector<T>:: to all occurrences of the old un-nested class name. The 'T' is placed on the first class' name only. Examples:

    Before:

    svector_iter<T>::svector_iter() : elem_ptr_(0)
    svector_iter<T>::svector_iter(pointer element) : elem_ptr_(element)
    typename svector_iter<T>::reference svector_iter<T>::operator*(void) const
    typename svector_iter<T>::pointer svector_iter<T>::operator->(void) const
    
    After:
    svector<T>::svector_iter::svector_iter() : elem_ptr_(0)
    svector<T>::svector_iter::svector_iter(pointer element) : elem_ptr_(element)
    typename svector<T>::svector_iter::reference svector<T>::svector_iter::operator*(void) const
    typename svector<T>::svector_iter::pointer svector<T>::svector_iter::operator->(void) const
    
  5. Finally, make the change in the client code:

    Before:

    svector_iter<int> it1;
    
    After:
    svector<int>::iterator it1;
    
One last note, if you want to simply name the iterator classes 'iterator' and 'const_iterator', then you won't need to add this typedef:
typedef svector_iter iterator;
typedef const_svector_iter const_iterator;
because the class names are already correct.

Modified partial header file
Modified partial implementation file
Driver


Extending the Iterator Classes

Ok, now we've got our svector working, lets start leveraging all of those generic algorithms.

Count the occurrences of a number: (revisited)

void f2(void)
{
  svector<int> cont1;

    // Fill vector: 0  1  2  3  4  5  6  7  8  9
  unsigned int i;
  for (i = 0; i < 10; i++)
    cont1.push_back(i);

    // Count the number of 5's in the vector
  int count = std::count(cont1.begin(), cont1.end(), 5);
  std::cout << "Count of 5: " << count << std::endl;
}
This leads to these compiler messages:

1. "_algobase.h": E2093 'operator!=' not implemented in type 'svector::svector_iter' 
     for arguments of the same type in function _STL::int count::svector_iter,int>
     (svector::svector_iter,svector::svector_iter,const int &) at line 533
2. "_algobase.h": E2096 Illegal structure operation in function 
     _STL::int count::svector_iter,int>(svector::svector_iter,svector::svector_iter,const int &)
     at line 533

Our version of count.

Here's the STL code that generates the errors:

// count
template <class _InputIter, class _Tp>
_STLP_INLINE_LOOP _STLP_DIFFERENCE_TYPE(_InputIter)
count(_InputIter __first, _InputIter __last, const _Tp& __value) {
  _STLP_DEBUG_CHECK(__check_range(__first, __last))
  _STLP_DIFFERENCE_TYPE(_InputIter) __n = 0;
  for ( ; __first != __last; ++__first) // error 1 (!=), 2 (++) 
    if (*__first == __value)          
      ++__n;
  return __n;
}
To fix these errors, we simply need to declare and implement these 2 methods in svector_iter. The code will then compile and run just fine printing:
Count of 5: 1

Declarations:

svector_iter& operator++(void);
bool operator!=(const svector_iter &rhs) const;

Implementation:

template <typename T> inline
typename svector<T>::svector_iter& svector<T>::svector_iter::operator++(void)
{
  ++elem_ptr_;
  return *this;
}

template <typename T> inline
bool svector<T>::svector_iter::operator!=(const svector_iter &rhs) const
{
  return elem_ptr_ != rhs.elem_ptr_;
}


Now we want to count the occurrences of even numbers in the vector using a function pointer. This works right-out-of-the-box:

bool IsEven(int value)
{
  return !(value % 2);
}

void f2(void)
{
    // Previous code ...
    // Fill vector: 0  1  2  3  4  5  6  7  8  9

    // Count the even numbers
  count = std::count_if(cont1.begin(), cont1.end(), IsEven);
  std::cout << "Even numbers: " << count << std::endl;
}

Output:
Even numbers: 5


Now we want to support the ability to sort the vector. Should we implement it in the class? We could, but why bother? Just use the std::sort generic algorithm:

std::sort(cont1.begin(), cont1.end());
But this doesn't quite work yet. We get 3 compiler errors due to missing methods. We need to implement these in svector_iter:
bool operator<(const svector_iter &rhs) const;
svector_iter operator-(int rhs) const;
svector_iter& operator--(void);
Note that not all STL implementations will require these methods. Some may choose to implement a sort function slightly differently and require more/less iterator functions.

Generic algorithms required by std::sort.

Now, this program works fine:

class cRandomInt1
{
  public:
    int operator()(void)
    {
      return rand() % 90 + 10;
    }
};

void f3(void)
{
  svector<int> cont1;

    // Fill vector: 0  1  2  3  4  5  6  7  8  9
  for (int i = 0; i < 10; i++)
    cont1.push_back(i);

    // do whatever

    // Replace with random values: 50  12  20  56  17  55  35  98  86  14
  std::generate(cont1.begin(), cont1.end(), cRandomInt1());
  print(cont1);

    // Sort them: 12  14  17  20  35  50  55  56  86  98
  std::sort(cont1.begin(), cont1.end());
  print(cont1);
}


A slightly larger example. This requires one additional method to compile.

#include <list>

int Random1_30(void)
{
  return rand() % 30 + 1;
}

void f4(void)
{
  typedef svector<int> ContainerType;

    // Create containers set to 0
  ContainerType cont1(10), cont2(10);

    // Create empty list containers
  std::list<int> cont3(10), cont4(10);

    // Fill cont1 with random values
    // 11  3  11  17  8  16  26  29  17  5
  std::generate(cont1.begin(), cont1.end(), Random1_30);
  print(cont1);

    // Copy values from cont1 to cont2
    // 11  3  11  17  8  16  26  29  17  5
  std::copy(cont1.begin(), cont1.end(), cont2.begin());
  print(cont2);

    // Reverse the elements
    // 5  17  29  26  16  8  17  11  3  11
  std::reverse(cont1.begin(), cont1.end());
  print(cont1);

    // Copy values from cont2 to cont3  (svector to list)
    // 11  3  11  17  8  16  26  29  17  5
  std::copy(cont2.begin(), cont2.end(), cont3.begin());
  print(cont3);

    // Copy a few values from cont1 to cont4
    // 0  0  0  29  26  16  8  17  0  0
  std::list<int>::iterator it = cont4.begin();
  std::advance(it, 3);
  std::copy(cont1.begin() + 2, cont1.end() - 3, it);
  print(cont4);
}
Complete interface for the iterators. (Not nested)


Extending the Container Class

Review the excerpts from the C++ Standards document regarding the Containers library and Iterators library. ISO/IEC 14882:1998(E)


A Closer Look at std::advance

We know that std::advance will not only work with several types of iterators, but it will be efficient will all of them. How can this be?

Here's an implementation of the advance function in the STL:

template<typename InputIter, typename DiffType> inline
void advance(InputIter& where, DiffType offset)
{ 
    // increment iterator by offset, arbitrary iterators
  _Advance(where, offset, Iter_cat(where));
}
An implementation of Iter_cat:


template<typename Iter> inline
typename iterator_traits<Iter>::iterator_category Iter_cat(const Iter&)
{ 
    // return category from iterator argument
  typename iterator_traits<Iter>::iterator_category cat;
  return cat;
}
And several overloaded _Advance functions:

template<typename InputIter, typename DiffType> inline
void _Advance(InputIter& where, DiffType offset, input_iterator_tag)
{ 
    // increment iterator by offset, input iterators
  for (; 0 < offset; --offset)
    ++where;
}

template<typename ForIter, typename DiffType> inline
void _Advance(ForIter& where, DiffType offset, forward_iterator_tag)
{ 
    // increment iterator by offset, forward iterators
  for (; 0 < offset; --offset)
    ++where;
}

template<typename BiIter, typename DiffType> inline
void _Advance(BiIter& where, DiffType offset, bidirectional_iterator_tag)
{ 
    // increment iterator by offset, bidirectional iterators
  for (; 0 < offset; --offset)
    ++where;
  for (; offset < 0; ++offset)
    --where;
}

template<typename RandIter, typename DiffType> inline
void _Advance(RandIter& where, DiffType offset, random_access_iterator_tag)
{ 
    // increment iterator by offset, random-access iterators
  where += offset;
}
An example for a container (using MSVC 7.1):
template <typename Iter> 
void insert(iterator it, Iter first, Iter last)
{
  Insert(it, first, last, std::_Iter_cat(first));
}

template<typename Iter>
void Insert(iterator it, Iter count, Iter val, std::_Int_iterator_tag)
{ 
  Insert_count(it, (size_type)count, (T)val);
}

template<class Iter>
void Insert(iterator it, Iter first, Iter last, std::input_iterator_tag)
{ 
  // The actual range insert
}

void Insert_count(iterator it, size_type n, const_reference value)
{
  // The actual inserting
}
The insert methods:
  // Inserts 'n' copies of 'value' before 'it'
void insert(iterator it, size_type n, const_reference value);

  // Inserts range before 'it'
template <typename Iter> 
void insert(iterator it, Iter first, Iter last);
Problems:

  // Same type, uses template, error: illegal indirection
CS270::slist<char> cont1;
char count = 5;
cont1.insert(cont1.begin(), count, 'A');

CS270::slist<int> cont1;
  // Same types, uses template, error: illegal indirection
cont1.insert(cont1.begin(), 5, 10);

  // Different types, no template
CS270::slist<int>::size_type s = 5;
cont1.insert(cont1.begin(), s, 10);

  // Different types, no template
cont1.insert(cont1.begin(), 
             static_cast<CS270::slist<int>::size_type>(5), 
             10);

  // Different types, no template
cont1.insert(cont1.begin(), 5u, 10);

  // Exact match, regular function is used
CS270::slist<unsigned> cont1;
cont1.insert(cont1.begin(), 5u, 10u);