Disclaimer: For the pedantic types (or pedants, if you want to be pedantic...), I generally refer to the word "overload" here as "Create functions that have the names begin
and end
and do using std::begin; using std::end;
.", which, believe me, is not tedious for me to write at all, but is very hard to read and is redundant to read. :p.
I'll basically give you the possible use-cases of such technique, and later my conclusion.
Case 1 - Your begin
and end
methods do not act like those of the standard containers
One situation where you may need to overload the std::begin
and std::end
functions is when you're using the begin
and end
methods of your type in a different way other than to provide iterator-like access to the elements of an object, and want to have overloads of std::begin
and std::end
call the begin and end methods used for iteration.
struct weird_container {
void begin() { std::cout << "Start annoying user." }
void end() { std::cout << "Stop annoying user." }
iterator iter_begin() { /* return begin iterator */ }
iterator iter_end() { /* return end iterator */ }
};
auto begin(weird_container& c) {
return c.iter_begin();
}
auto end(weird_container& c) {
return c.iter_end();
}
However, you wouldn't and shouldn't do such a crazy thing as range-for would break if used with an object of weird_container
, as per rules of range-for, the weird_container::begin()
and weird_container::end()
methods would be found before the stand-alone function variants.
This case therefore brings an argument not to use what you have proposed, as it would break one very useful feature of the language.
Case 2 - begin
and end
methods aren't defined at all
Another case is when you don't define the begin
and end
methods. This is a more common and applicable case, when you want to extend your type to be iteratable without modifying the class interface.
struct good_ol_type {
...
some_container& get_data();
...
};
auto begin(good_ol_type& x) {
return x.get_data().begin();
}
auto end(good_ol_type& x) {
return x.get_data().end();
}
This would enable you to use some nifty features on good_ol_type
(algorithms, range-for, etc) without actually modifying its interface! This is in line with Herb Sutter's recommendation of extending the functionality of types through non-member non-friend functions.
This is the good case, the one where you actually want to overload std:;begin
and std::end
.
Conclusion
As I haven't ever seen someone do something like that of the first case (except for my example), then you'd really want to use what you've proposed and overload std::begin
and std::end
wherever applicable.
I did not include here the case where you defined both begin
and end
methods, and begin
and end
functions that does different things than the methods. I believe such a situation is contrived, ill-formed and/or done by a programmer who haven't had much experience delving into the debugger or reading novel template errors.