The cleanest way, at least at the time of use, is to specify your type for a special iteration.
Firstly, some mechanisms:
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 sign that says “iterate weird,” and the overload starts / ends:
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 the point of use:
std::string s = "hello world"; for( char c : mark_type<strange_iteration>(s) ) { std::cout << c; } std::cout << "\n";
with one entry that I wrote mark_type to be too general.
Now mark_type<Foo> will create links to lvalues and create a moved copy of rvalue if it is passed to it. In an iteration, the lifetime of the return value will be increased by extending the life of the link.
You can use this technique to do something like
for( char c : mark_type<reverse_iteration>(s) )
where are we instead iterating back now, regardless of the container we passed into. A “create copy” for rvalue is required for such constructs:
for( char c: mark_type<reverse_iteration>(mark_type<strange_iteration>(s))
where we connect the labels. The extension of the service life extends only to the most remote return value, and our "create a copy and move" by rvalue is basically an extension of the service life manually.
Finally, the use of std::begin in the above code is best done in the context of ADL tolerance in return values. Create a helper namespace as follows:
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 with decltype in my previous code with adl_helper::adl_begin , which emulates how for( a:b ) loops find begin and end touch better (not perfect, but better).
C ++ 1y may come with some machines to remove the need for the aforementioned hack.
Code execution example: http://ideone.com/RYvzD0