Introduction to C++ templates pt.1

This series is intended for beginner C++ programmers, or those who feel a little rusty about their C++ programming skills in the wake of the new standard.

Thanks to C++11 it’s much easier to create templates. Especially that we can now create variadic templates that allow us to generically handle multiple arguments (in form of types and values). There is much knowledge about generic programming and I will not pretend to know everything there is to know. Never the less, I will try my best to share the best tips & tricks with you.

Before I jump into the task and it’s template solution I will talk for a while (this part) about iterations/loops in C++, which is required before we move onto templates themselves (in the next parts).

Calamity of abundance

Everyone of you is (or at least, should be) familiar with a for loop.

for (int i = 0; i < 10; ++i) { ... }

Above loop body is run exactly 10 times (I am assuming the body isn’t modifying the index). Most of the times, however, we don’t want/have to specify the loop count as we just iterate over a container and process its elements. C++11 offers many ways to do it.

std::vector<int> foo { 2, 4, 6, 8 };

// ver. A C++98
for (std::vector<int>::constant_iterator
     it = foo.begin();
     it != foo.end();
     ++it)
{
    std::cout
        << *it /* get the value pointed by iterator */
        << std::endl;
}

// ver. B C++11
for (auto it = foo.begin(); it != foo.end(); ++it) {
    std::cout << *it << std::endl;
}

// ver. C C++11
for (int /* can be auto */ val : foo) {
    std::cout << val << std::endl;
}

// ver. D C++11
auto end = std::end(foo);
for (auto it = std::begin(foo); it != end; ++it) {
    std::cout << *it << std::endl;
}

As you can see there are many ways to iterate over a collection. Before we start discussing the loops, notice the – so called – brace initialization of std::vector. If you are a little confused, think of it as a syntactic sugar to allow similar initialization that is allowed for C arrays(int[]) and structs.

Iterators

In all the loop examples you can see (directly, or indirectly as in ver. C) the usage of iterators. Iterators are a basic concept in C++ which allows decoupling of the underlying container and way of retrieving it’s values, or iterating over them. I don’t want to repeat what it’s clearly described at C++ reference, so please take a look there, first.

To add some value what you have just read, hear are some popular examples of iterators:

  • InputIterator: Input TCP package stream where the connection provider throws the underlying buffer away after your iterations.
  • OutputIterator: Output TCP package stream that, right after writing to an element, sends it to the client and doesn’t allow to rewrite the same package again.
  • ForwardIterator (mutable): Single linked list, where (due to only one pointer) you are only allowed to go forward. The underlying elements won’t be invalidated so you can safely reiterate the whole sequence again.
  • BidirectionalIterator (mutable): Double linked list which – duh! – allows to traverse back and forth multiple times.
  • RandomAccessIterator (mutable): Arrays/vectors/(hash)maps which allow direct access to elements so you don’t have to iterate from beginning 8 times to get to 8th element. Just do *(it + 8) to get there in constant time.

What’s important to remember is that the more advanced iterator you wish to use, the less containers will be able to comply to the requirements. Thus if you can, you should target weaker iterators (think of targeting elderly Windows XP, and not limiting your users to Windows 8.1), and optionally allows some optimizations for stronger iterators.

Free functions

Take a look at version C of the loop. Version D presents an “unrolled” version C. Apart from the end iterator optimization (calculate it only once, as you won’t be able to modify the underlying sequence), the two important functions are std::begin() and std::end(). These are so called free functions which are overloaded so that they can take either a C array, any object that has a begin() member, or std::valarray / std::initializer_list objects (which don’t provide begin member).

These free functions allow to take suitable iterators from a vast array of object types which greatly eases the way of retrieving iterators from various containers.

In the next part we will feast upon std::for_each specification…

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: