Keywords: ISO C++ , library

Chapter 18. Interacting with C
Prev | Part VII. Containers | Next

## Chapter 18. Interacting with C

## Containers vs. Arrays

You're writing some code and can't decide whether to use builtin arrays or some kind of container. There are compelling reasons to use one of the container classes, but you're afraid that you'll eventually run into difficulties, change everything back to arrays, and then have to change all the code that uses those data types to keep up with the change.

If your code makes use of the standard algorithms, this isn't as scary as it sounds. The algorithms don't know, nor care, about the kind of “container” on which they work, since the algorithms are only given endpoints to work with. For the container classes, these are iterators (usually `begin()` and `end()`, but not always). For builtin arrays, these are the address of the first element and the past-the-end element.

Some very simple wrapper functions can hide all of that from the rest of the code. For example, a pair of functions called `beginof` can be written, one that takes an array, another that takes a vector. The first returns a pointer to the first element, and the second returns the vector's `begin()` iterator.

The functions should be made template functions, and should also be declared inline. As pointed out in the comments in the code below, this can lead to `beginof` being optimized out of existence, so you pay absolutely nothing in terms of increased code size or execution time.

The result is that if all your algorithm calls look like

       std::transform(beginof(foo), endof(foo), beginof(foo), SomeFunction);

then the type of foo can change from an array of ints to a vector of ints to a deque of ints and back again, without ever changing any client code.

    // beginof
    template<typename T>
      inline typename vector<T>::iterator
      beginof(vector<T> &v)
      { return v.begin(); }

    template<typename T, unsigned int sz>
      inline T*
      beginof(T (&array)[sz]) { return array; }

    // endof
    template<typename T>
      inline typename vector<T>::iterator
      endof(vector<T> &v)
      { return v.end(); }

    template<typename T, unsigned int sz>
      inline T*
      endof(T (&array)[sz]) { return array + sz; }

    // lengthof
    template<typename T>
      inline typename vector<T>::size_type
      lengthof(vector<T> &v)
      { return v.size(); }

    template<typename T, unsigned int sz>
      inline unsigned int
      lengthof(T (&)[sz]) { return sz; }

Astute readers will notice two things at once: first, that the container class is still a `vector<T>` instead of a more general `Container<T>`. This would mean that three functions for `deque` would have to be added, another three for `list`, and so on. This is due to problems with getting template resolution correct; I find it easier just to give the extra three lines and avoid confusion.

Second, the line

        inline unsigned int lengthof (T (&)[sz]) { return sz; }

looks just weird! Hint: unused parameters can be left nameless.

Prev | Up | Next
bitset | Home | Part VIII. Iterators