Reputation: 6390
In page 82 of the draft of Chapter 3 : A Tour of C++: Abstraction Mechanisms the author writes:
If we also want to use the range-for loop for our Vector, we must define suitable begin() and end() functions:
template<typename T>
T∗ begin(Vector<T>& x)
{
return &x[0]; // pointer to first element
}
template<typename T>
T∗ end(Vector<T>& x)
{
return x.begin()+x.size(); // pointer to one-past-last element
}
Given those, we can write:
void f2(const Vector<string>& vs) // Vector of some strings
{
for (auto s : vs)
cout << s << ’\n’;
}
Notice that the class template Vector is defined in page 81 of the draft.
Upvotes: 0
Views: 613
Reputation: 55415
For range-based for to work, the compiler needs to find a suitable function to get the iterators.
If the type used is a class, it'll first look for member functions begin
and end
in the scope of that class.
If the type is not a class or the are no such member functions, it looks them up by Argument Dependent Lookup.
This is the reason range-based for works on C-arrays. Obviously, arrays can't have member functions, so standard library provides two functions defined similarly to this:
template<typename T, size_t N>
T* begin( T(&array)[N] )
{
return array;
}
and similarly for end
.
To answer your question from the title: they can be, but it's not a neccesity. You can define free functions in the same namespace as your class and they'll be found.
Upvotes: 5
Reputation: 24956
In such a range based for:
for ( for-range-declaration : expression ) statement
the standard says that the compiler looks for either members ((expression).begin()
, (expression).end()
) or free functions (begin((expression))
, end((expression))
) if expression is of class type.
Therefore you can either provide member functions OR free functions (which need to be in scope for argument dependant lookup).
C++11, § 6.5.4 [stmt.ranged]
This is what a ranged-based for does according to the standard:
for ( for-range-declaration : expression ) statement
range-init = ( expression )
{
auto && __range = range-init;
for ( auto __begin = begin-expr,
__end = end-expr;
__begin != __end;
++__begin )
{
for-range-declaration = *__begin;
statement
}
}
The begin-expr
and end-expr
are described as:
if
_RangeT
is an array type, begin-expr and end-expr are _range and _range + _bound, respectively, where _bound is the array bound. If _RangeT is an array of unknown size or an array of incomplete type, the program is ill-formed;if
_RangeT
is a class type, the unqualified-ids begin and end are looked up in the scope of class _RangeT as if by class member access lookup (3.4.5), and if either (or both) finds at least one declaration, beginexpr and end-expr are __range.begin() and __range.end(), respectively;otherwise, begin-expr and end-expr are
begin(__range)
andend(__range)
, respectively, wherebegin
andend
are looked up with argument-dependent lookup (3.4.2). For the purposes of this name lookup, namespace std is an associated namespace.
Upvotes: 2
Reputation: 14510
If it is not an array or a container with .begin()
and .end()
it will look up by Argument dependent name.
It is said here :
Keep in mind these facts about range-based for:
Automatically recognizes arrays.
Recognizes containers that have .begin() and .end().
Uses argument-dependent lookup begin() and end() for anything else.
Upvotes: 4