The cleanest way to do, at least at the point of use, this is to mark up your type for the purpose of special iteration.
First, some machinery:
template<class Mark, class T>
struct marked_type {
T raw;
marked_type(T&& in):raw(std::forward<T>(in)) {}
};
template<typename Mark, typename T>
marked_type<Mark, T> mark_type( T&& t ) {
return {std::forward<T>(t)};
}
next, we invent a mark that says "iterate strangely", and overload begin/end:
struct strange_iteration {};
template<typename T>
auto begin( marked_type<strange_iteration, T> const& container )
-> decltype( std::begin(std::forward<T>(container.raw)) )
{
std::cout << "BEGIN";
using std::begin;
return begin(std::forward<T>(container.raw));
}
template<typename T>
auto end( marked_type<strange_iteration, T> const& container )
-> decltype( std::end(std::forward<T>(container.raw)) )
{
std::cout << "END";
using std::end;
return end(std::forward<T>(container.raw));
}
and then at point of use:
std::string s = "hello world";
for( char c : mark_type<strange_iteration>(s) ) {
std::cout << c;
}
std::cout << "\n";
with the one note that I wrote mark_type
to be overly generic.
Now, mark_type<Foo>
will create references to lvalues, and create a moved-to copy of an rvalue, if passed to it. In an iteration, its return value's lifetime will be extended by reference lifetime extension.
You can use this technique to do things like
for( char c : mark_type<reverse_iteration>(s) )
where now we instead iterate backwards, regardless of the container we have passed in. The "creation of a copy" for rvalue is needed for constructs like this:
for( char c: mark_type<reverse_iteration>(mark_type<strange_iteration>(s))
where we daisy-chain the marks. Lifetime extension only applies to the outermost return value, and our "create a copy and move" on rvalue is basically manual lifetime extension.
Finally, the std::begin
use in the above code is better done in an ADL-admitting context in the return values. Create a helper namespace like this:
namespace adl_helper {
using std::begin; using std::end;
template<typename T>
auto adl_begin(T&& t)->decltype( begin(std::forward<T>(t)) ); // no implementation
template<typename T>
auto adl_end(T&& t)->decltype( end(std::forward<T>(t)) ); // no implementation
// add adl_cbegin, adl_rbegin etc in C++14
}
then replace std::begin
in the decltype
s in my above code with adl_helper::adl_begin
, which emulates how for( a:b )
loops find begin
and end
a touch better (not perfectly, but better).
C++1y may come with some machinery to remove the need for the above hack.
Sample code running: http://ideone.com/RYvzD0