C++11 has a new way of doing for loops. Well, "new" might be a bit subjective, seeing as how Perl has had foreach since like 1994. But whatever. The standards committee took their time, attended a lot of meetings in exotic locations [Indiana], and made sure that the new range-based for loops wouldn't turn into another exception specification. The result is a dramatic improvement over what C++03 offered:
std::set<char> letters = {'o', 'm', 'g'}; for (char c: letters) { // range-based for loop std::cout << c << '\n'; }
But how does it work?
Under the covers, when the compiler sees a statement of the form:
for (range-declaration: expression) statement
...it translates it into something like:
{ auto && __range = range-init; for (auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin) { range-declaration = *__begin; statement } }
range-init
is usually ( expression )
unless we're iterating over an initializer list, in which case it's just the raw list.
If the type of expression
is an array, then begin-expr
is __range
and end-expr
is __range + __bound
, where __bound
is the size of the array.
char letters[] = {'o', 'm', 'g'}; for (char c: letters) { // works for arrays, too std::cout << c << '\n'; }
Otherwise, begin-expr
is std::begin(__range)
and end-expr
is std::end(__range)
.
Iterating over custom types
std::begin
and std::end
look for begin/end methods that expose iterators, so you can iterate over custom types as long as they're well-behaved:
#include <iostream> #include <algorithm> #include <memory> template <typename T> class Foo { public: Foo(const std::initializer_list<T> &init) : m_size(init.size()), m_data(new T[init.size()]) { std::copy(init.begin(), init.end(), m_data.get()); } // expose begin/end for iteration support T* begin() { return m_data.get(); } T* end() { return m_data.get() + m_size; } private: size_t m_size; std::unique_ptr<T[]> m_data; }; int main() { Foo<int> foo = {3, 1, 4, 1, 5, 9}; for (auto c: foo) { std::cout << c << '\n'; } }
Iterating over initializer lists
std::initializer_list
exposes begin and end methods, so it can be iterated over:
for (auto c: {3, 1, 4, 1, 5, 9}) { std::cout << c << '\n'; }