C++ catches up with constructs common in Java and C# with the new range-based for loop.
A simple example should suffice to demonstrate its common use.
TEST(ARangeBasedForLoop, CanIterateOverAVector) {
vector collectedNumbers;
vector numbers{1, 2, 3};
for (int each: numbers)
collectedNumbers.push_back(each);
ASSERT_THAT(collectedNumbers, Eq(vector { 1, 2, 3}));
}
It’s always nice to know the appropriate names for the syntactical parts of things. Here’s an overly clever test that shows what things are called in the range-based for loop.
TEST(ARangeBasedForLoop, HasAppropriateNamesForItsSyntacticalParts) {
typedef int type_specifier_seq;
vector expression{1, 2, 3, };
vector collectedNumbers;
auto statement = [&] (int each) -> void { collectedNumbers.push_back(each); };
for (type_specifier_seq simple_declarator: expression) statement(simple_declarator);
ASSERT_THAT(collectedNumbers, Eq(vector { 1, 2, 3}));
}
The range-based for loop supports iterating over a number of common things. Here are a few.
TEST(ARangeBasedForLoop, CanIterateOverAnArray) {
vector collectedNumbers;
int numbers[]{1, 2, 3};
for (int each: numbers)
collectedNumbers.push_back(each);
ASSERT_THAT(collectedNumbers, Eq(vector { 1, 2, 3}));
}
TEST(ARangeBasedForLoop, CanIterateOverAMap) {
map dictionary\{\{1, "uno"\}, \{2, "dos"\}\};
map collectedDictionary;
for (pair pair: dictionary)
collectedDictionary[pair.first] = pair.second;
ASSERT_THAT(collectedDictionary, Eq(map\{\{1, "uno"\}, \{2, "dos"\}\}));
}
TEST(ARangeBasedForLoop, CanIterateOverAString) {
vector collectedChars;
for (char each: "abc")
collectedChars.push_back(each);
ASSERT_THAT(collectedChars, Eq(vector{ 'a', 'b', 'c', 0 }));
}
I’ve shown explicit type information in the prior examples. You should usually prefer auto, instead.
TEST(ARangeBasedForLoop, ShouldPreferAnInferencingTypeSpecifier) {
vector collectedNumbers;
for (auto each: {1, 2, 3})
collectedNumbers.push_back(each);
ASSERT_THAT(collectedNumbers, Eq(vector{ 1, 2, 3}));
}
If you want to update each element as you iterate…
TEST(ARangeBasedForLoop, CanAccessEachElementByReference) {
vector strings{"a", "b"};
for (auto& each: strings)
each += each;
ASSERT_THAT(strings, Eq(vector{ "aa", "bb" }));
}
Or if you want better performance, but want to disallow modifying each element.
TEST(ARangeBasedForLoop, CanAccessEachElementByConstReference) {
vector strings{"a", "b"};
vector collectedStrings;
for (const auto& each: strings)
// each += each; // fails compilation
collectedStrings.push_back(each);
ASSERT_THAT(strings, Eq(vector{ "a", "b" }));
}
The range-based for loop can iterate over anything that implements begin() and end() (each returning an iterator object–something that implements operator!=, operator*, and operator++). Here is a simple class that supports iteration across a range of numbers, (also supporting the ability to skip elements).
class IntSequence {
public:
IntSequence(int start, int stop, int by=1): start_{start}, stop_{stop}, by_{by} {}
class iterator {
public:
iterator(int value, int by) : current_(value), by_(by) {}
bool operator!=(const iterator& rhs) const {
return current_ <= rhs.current_;
}
int& operator*() { return current_; }
iterator operator++() {
current_ += by_;
return *this;
}
private:
int current_;
int by_;
};
iterator begin() { return iterator(start_, by_); }
iterator end() { return iterator(stop_, by_); }
private:
int start_;
int stop_;
int by_;
};
And here's an example demonstrating use:
TEST(ARangeBasedForLoop, CanIterateOverAnythingImplementingBeginAndEnd) {
vector collectedNumbers;
int start{3};
int stop{10};
int by{2};
IntSequence sequence(start, stop, by);
for (auto each: sequence)
collectedNumbers.push_back(each);
ASSERT_THAT(collectedNumbers, Eq(vector{3, 5, 7, 9}));
}
(This example could be construed as contrived: Why not simply use a regular ol' for loop? Most of the time, that idiom is simple and probably preferred, but you might find value in the ability to pass around a sequence concept to other functions, or to serialize it, or otherwise use it where having an object abstraction might simplify code.)
Note: Code build using gcc 4.7.2 under Ubuntu.