Let's say we need to iterate over a container. The traditional for loop would look like this:
for (auto it = container.begin(), end = container.end();
it != end;
++it)
{
doStuff(*it);
}
while the range-based for would look like this:
for (auto& element : container)
{
doStuff(element);
}
Now, at some point of the development we realize that for some reason or other, we need to increment something else over those loop iterations.
What needs to be incremented may be various things. For example, if we have relevant data stored in other containers of the same size, we may need to increment iterators to those containers too as we go through the iterations (though I hope future version of the standard library will allow us to do that more expressively, through structured bindings and a standard version of boost::range::combine or something).
In the following, to keep things simple, we'll assume that we want to attribute an ID to each element, so what needs to be incremented is simply a counter.
The traditional loop would now look like this:
unsigned int elementID = 0u;
for (auto it = container.begin(), end = container.end();
it != end;
++it, ++elementID)
{
doStuff(*it, elementID);
}
Barely anything has to be changed, and adding the ++elementID after the ++it guarantees that the counter will be incremented no matter what after each iteration. Even if another programmer were to modify the body of the loop and, say, go early to the next iteration under certain conditions through continue, there would be no risk of them forgetting to increment the counter or anything like that.
Now, with the range-based for, as far as I can tell, the only way to do the incrementation would be to do something like this:
unsigned int elementID = 0u;
for (auto& element : container)
{
doStuff(element, elementID);
++elementID;
}
That is to say, to put the incrementation within the body of the loop.
This is less expressive with regards to elementID (i.e. if the body of the code is long, someone reading the code will not see at a glance that we're iterating over elementID too), and it doesn't give the guarantee that I've mentioned above, so it is also prone to error.
Is there really no other way to implement this with a range-based for? Or is there a way to write something along the lines of for(auto& element : container; ++elementID){...} that I'm simply not aware of?
Edit after people answered
Nevin suggested boost's BOOST_SCOPE_EXIT_ALL, which is the closest to what I had in mind as far as non-native solutions go.
I'm not sure about the actual implementation, but I guess this relies on lambdas and destructors. I wrote this to test it out:
template <typename T>
class ScopeExitManager
{
public:
ScopeExitManager(T const& functionToRunOnExit) : _functionToRunOnExit(functionToRunOnExit)
{
}
~ScopeExitManager()
{
_functionToRunOnExit();
}
private:
T _functionToRunOnExit;
};
template <typename T>
ScopeExitManager<T> runOnScopeExit(T const& functionToRunOnExit)
{
return {functionToRunOnExit};
}
Which then allowed me to write something along the lines of:
unsigned int elementID = 0u;
for (auto& element : container)
{
// Always at the beginning of the loop
auto scopeExitManager = runOnScopeExit([&elementID](){++elementID;});
// Actual body of the loop
doStuff(element, elementID);
}
which is expressive and guarantees that elementID will be incremented. This is great!