Like many people these days I have been trying the different features that C++11 brings. One of my favorites is the \"range-based for loops\".
I understand that:
I write my answer because some people might be more happy with simple real life example without STL includes.
I have my own plain only data array implementation for some reason, and I wanted to use the range based for loop. Here is my solution:
template <typename DataType>
class PodArray {
public:
class iterator {
public:
iterator(DataType * ptr): ptr(ptr){}
iterator operator++() { ++ptr; return *this; }
bool operator!=(const iterator & other) const { return ptr != other.ptr; }
const DataType& operator*() const { return *ptr; }
private:
DataType* ptr;
};
private:
unsigned len;
DataType *val;
public:
iterator begin() const { return iterator(val); }
iterator end() const { return iterator(val + len); }
// rest of the container definition not related to the question ...
};
Then the usage example:
PodArray<char> array;
// fill up array in some way
for(auto& c : array)
printf("char: %c\n", c);
I would like to elaborate some parts of @Steve Jessop's answer, for which at first I didn't understand. Hope it helps.
std::begin
calls thebegin()
member function anyway, so if you only implement one of the above, then the results should be the same no matter which one you choose. That's the same results for ranged-based for loops, and also the same result for mere mortal code that doesn't have its own magical name resolution rules so just doesusing std::begin;
followed by an unqualified call tobegin(a)
.If you implement the member functions and the ADL functions, though, then range-based for loops should call the member functions, whereas mere mortals will call the ADL functions. Best make sure they do the same thing in that case!
https://en.cppreference.com/w/cpp/language/range-for :
- If ...
- If
range_expression
is an expression of a class typeC
that has both a member namedbegin
and a member namedend
(regardless of the type or accessibility of such member), thenbegin_expr
is__range.begin(
) andend_expr
is__range.end()
;- Otherwise,
begin_expr
isbegin(__range)
andend_expr
isend(__range)
, which are found via argument-dependent lookup (non-ADL lookup is not performed).
For range-based for loop, member functions are selected first.
But for
using std::begin;
begin(instance);
ADL functions are selected first.
Example:
#include <iostream>
#include <string>
using std::cout;
using std::endl;
namespace Foo{
struct A{
//member function version
int* begin(){
cout << "111";
int* p = new int(3); //leak I know, for simplicity
return p;
}
int *end(){
cout << "111";
int* p = new int(4);
return p;
}
};
//ADL version
int* begin(A a){
cout << "222";
int* p = new int(5);
return p;
}
int* end(A a){
cout << "222";
int* p = new int(6);
return p;
}
}
int main(int argc, char *args[]){
// Uncomment only one of two code sections below for each trial
// Foo::A a;
// using std::begin;
// begin(a); //ADL version are selected. If comment out ADL version, then member functions are called.
// Foo::A a;
// for(auto s: a){ //member functions are selected. If comment out member functions, then ADL are called.
// }
}
Chris Redford's answer also works for Qt containers (of course). Here is an adaption (notice I return a constBegin()
, respectively constEnd()
from the const_iterator methods):
class MyCustomClass{
QList<MyCustomDatatype> data_;
public:
// ctors,dtor, methods here...
QList<MyCustomDatatype>::iterator begin() { return data_.begin(); }
QList<MyCustomDatatype>::iterator end() { return data_.end(); }
QList<MyCustomDatatype>::const_iterator begin() const{ return data_.constBegin(); }
QList<MyCustomDatatype>::const_iterator end() const{ return data_.constEnd(); }
};