Hello, my name is Jonathan Boccara. I'm your host on Fluent C++. I have been a C++ developer for 6 years, working for Murex which is a major software editor in the finance industry. My focus is on C++ and particularly how to write expressive code. I'm happy to take your feedback, don't hesitate to drop a comment on a post, follow me or get in touch directly !
Jonathan Boccara's blog
As a logical part of the STL learning resource, here is how the STL has been designed, and how you can design your components to make them benefit from the power of the STL.
The design of the STL has been driven by the intention of separating algorithms from data structures.
Data can even be drawn from a stream as we see in How to split a string in C++.
The intention to separate algorithms from data structures has been accomplished with an interface: iterators.
In order to benefit from all the advantages offered by the large variety of algorithms, data has to present an Iterator interface.
Here we show how to do this for different kinds of data.
Iterators can be obtained via:
For C arrays, pointers play the role of iterators.
std::for_each(myInts, myInts + 100, doSomething);
Even if strictly speaking,
myInts is not a pointer but an array, it still gives access to the first element of the array, while
myInts + 100 points to the “one-after-the-end” address, which follows the begin-end semantics.
So C arrays can be used with algorithms, which can be of great help in legacy code.
Note that a new uniform syntax has been introduced since C++11, with
std::end) free functions (and not class methods). They can be used uniformly on any type showing a
end) method that can be called with no argument, and they can also be used on C arrays.
The following code gives an example of this uniformity:
std::vector<int> vec(100, 0); // vector of size 100 initialized with zeros
std::for_each(std::begin(vec), std::end(vec), doSomething);
std::for_each(std::begin(myInts), std::end(myInts), doSomething);
This makes C arrays easier to use, and is quite convenient for generic code.
Note that the
std namespace has to be written explicitly for the C array, because it cannot use ADL, but could be omitted on the vector. More on ADL on in a later post.
Sometimes we write our own collection that reflect domain needs. Let’s take the example of the user-defined FlowCollection class, that represents a collection of financial flows. Given what we saw above, it needs to publish iterators in order to benefit for algorithms. How do we do this?
Every time you want to write a collection, ask yourself if a standard one won’t do. This would be as much code that you don’t write. In quite a few cases a standard collection suffices, and you can put a domain name on it with a typedef. For exemple for our collection of flows:
using FlowCollection = std::vector<Flow>;
This way you get all the iterators plus all the functionalities of std::vector for free, while having a type with a domain name.
If a domain functionality is really necessary for the collection, or if you only want a subpart of what a standard container offers, you may have to define a class that wraps a standard container. In this case, iterators can be implemented with the standard container’s iterators:
// ...domain interface...
// iterators to allow data access
using const_iterator = std::vector<Flow>::const_iterator;
const_iterator begin() const;
const_iterator end() const;
// iterators to allow data modification
using iterator = std::vector<Flow>::iterator;
// other iterators...
// ...domain data...
If your collection has such a degree of complexity that the two previous techniques won’t do, you may have to implement your own iterators. This is more complex to do and outside the scope of this post, and the occasions for such a need should be very rare.
This is where the STL stands in the C++ of today (<= C++17). To have a glimpse of how the STL is shaping for the future (and to see how you can start using it right now), hop over to ranges.
Related articles:    Don't want to miss out ? Follow: