The changing face of programming abstraction in C++

Iterating through containers… options, options options.

Iterating through a container of objects is something we do a lot.  It’s boilerplate code.  It’s also a nice indicator of how C++ is raising the level of abstraction in programming.

So let’s start with a simple container of ADTs:

image

Now let’s iterate through them, calling the mf() method on each object in turn.  First, we’ll ‘roll our own’ loop.

image

That’s imperative programming in action; and has the warm, fuzzy feeling of recognition for all C programmers.

Modern C++, however, favours a more declarative style of programming (even though, under the hood, it’s all imperative!)  Typically, you’d use an algorithm to declare what you want to happen (in this case, iterate across a range of objects in a container, doing ‘something’ to each one) rather than how:

image

This is certainly more abstract, but I’m not sure it’s much more readable (am I the only person who feels the member function adapters are named the wrong way round?)

A brief look at the code for for_each reveals that it pretty much does what your hand-rolled code would do.

With the release of C++11 we have new options available to us.  First up is the lambda:

image

The syntax for lambdas tends to provoke upset in more-sensitive souls but once you’re used to it the algorithms become quite elegant: I define what I want to happen exactly at the point I need it.  Now I’m specifying what I want to achieve, and how I want to achieve it all in one one lovely confection of declarative and imperative code!

Finally, in a random act of good sense the C++ committee realised that iterating through containers is such a common thing to do it should be added to the language rather than being relegated to a library function – just like in other modern programming languages:

image

The range-for statement provides a single (compound) statement solution to accessing each element of a container.  And it works with all the standard containers – even good ol’ fashioned arrays.

But surely, such an abstract statement will be woefully inefficient; or put another way: “it can’t possibly be as efficient as my hand-rolled loop”.  I did a quick comparison:

image

Hand-rolled loop:

  • 35 lines of assembler, including:
  • 8 function calls
    • std::vector<T>::begin
    • std::vector<T>::end
    • std::vector<T>::iterator::operator ++
    • std::vector<T>::iterator::operator !=
    • std::vector<T>::iterator::operator –>
    • std::vector<T>::iterator::~iterator (called in two places)
    • The member function call

Range-for statement:

  • 34 lines of assembler, including:
  • 8 function calls:
    • std::vector<T>::begin()
    • std::vector<T>::end()
    • std::vector<T>::iterator::operator ++
    • std::vector<T>::iterator::operator !=
    • std::vector<T>::iterator::operator *
    • std::vector<T>::iterator::~iterator (called in two places)
    • The member function call.

So, to all practical intents and purposes the code is the same.  So, with C++11 there really is no compelling reason for rolling your own iteration loops.

Glennan Carnie

Technical Consultant at Feabhas Ltd
Glennan is an embedded systems and software engineer with over 20 years experience, mostly in high-integrity systems for the defence and aerospace industry.

He specialises in C++, UML, software modelling, Systems Engineering and process development.

Posted on July 27th, 2012
» Feed to this thread
» Trackback

Leave a Reply