Page 29 - MSDN Magazine, October 2017
P. 29

the values so that I can iterate over them. What if I need a lot more values? I’d much rather just write the for loop myself:
#include <stdio.h>
int main() {
for (int i = 0; i != 10; ++i) {
printf("%d\n", i); }
}
To add insult to injury, this involves a lot less typing. It sure would be nice, however, if there were an iota-like function that could somehow generate a range of values for a range-based for loop to consume without having to use a container. I was recently browsing a book about the Python language and noticed that it has a built-in function called range. I can write the same program in Python like this:
for i in range(0, 10): print(i)
Be careful with that indentation. It’s how the Python language represents compound statements. I read that Python was named after a certain British comedy rather than the nonvenomous snake. I don’t think the author was kidding. Still, I love the succinct nature of this code. Surely, I can achieve something along these lines in C++. Indeed, this is what I wish the iota algorithm would provide but, alas. Essentially, what I’m looking for is a range algorithm that looks something like this:
template <typename T>
generator<T> range(T first, T last) {
return{ ... }; }
int main() {
for (int i : range(0, 10)) {
printf("%d\n", i); }
}
To my knowledge, no such function exists, so let’s go and build it. The first step is to approximate the algorithm with something reliable that can act as a baseline for testing. The C++ standard vector container comes in handy in such cases:
#include <vector>
template <typename T>
std::vector<T> range(T first, T last) {
std::vector<T> values;
while (first != last) {
values.push_back(first++); }
return values; }
It also does a good job of illustrating why you don’t want to build a container in the first place, or even figure out how large it should be, for that matter. Why should there even be a cap? Still, this is useful because you can easily compare the output of this range generator to a more efficient alternative. Well, it turns out that writing a more efficient generator isn’t that difficult. Have a look at Figure 1.
The range function simply creates a generator initialized with the same pair of bounding values. The generator can then use those msdnmagazine.com
Figure 1 A Classical Generator
template <typename T> struct generator
{
T first; T last;
struct iterator{ ... };
iterator begin() {
return{ first }; }
iterator end() {
return{ last }; }
};
template <typename T>
generator<T> range(T first, T last) {
return{ first, last }; }
values to produce lightweight iterators via the conventional begin and end member functions. The most tedious part is spitting out the largely boilerplate iterator implementation. The iterator can simply hold a given value and increment it as needed. It must also provide a set of type aliases to describe itself to standard algorithms. This isn’t strictly necessary for the simple range-based for loop, but it pays to include this as a bit of future-proofing:
template <typename T> struct generator
{
struct iterator {
T value;
using iterator_category = std::input_iterator_tag; using value_type = T;
using difference_type = ptrdiff_t;
using pointer = T const*;
using reference = T const&;
Incrementing the iterator can simply increment the underlying value. The post-increment form can safely be deleted:
iterator& operator++() {
++value;
return *this; }
iterator operator++(int) = delete;
The other equally important function provided by an iterator is that of comparison. A range-based for loop will use this to deter- mine whether it has reached the end of the range:
bool operator==(iterator const& other) const {
return value == other.value; }
bool operator!=(iterator const& other) const {
return !(*this == other);
}
Finally, a range-based for loop will want to dereference the iter- ator to return the current value in the range. I could delete the member call operator, because it isn’t needed for the range-based for loop, but that would needlessly limit the utility of generators to be used by other algorithms:
October 2017 25


































































































   27   28   29   30   31