至少在使用时,最干净的方法是标记您的类型以用于特殊迭代。
首先,一些机械:
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)};
}
接下来,我们发明了一个“奇怪地迭代”的标记,并重载开始/结束:
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));
}
然后在使用点:
std::string s = "hello world";
for( char c : mark_type<strange_iteration>(s) ) {
std::cout << c;
}
std::cout << "\n";
我写的一张纸条mark_type
过于笼统。
现在,mark_type<Foo>
将创建对左值的引用,并创建一个右值的移动副本(如果传递给它)。在一次迭代中,它的返回值的生命周期将通过引用生命周期延长来延长。
您可以使用此技术执行以下操作
for( char c : mark_type<reverse_iteration>(s) )
现在我们改为向后迭代,而不管我们传入的容器是什么。像这样的构造需要为右值“创建副本”:
for( char c: mark_type<reverse_iteration>(mark_type<strange_iteration>(s))
我们以菊花链方式连接标记。延寿只适用于最外层的返回值,而我们对右值的“创建副本并移动”基本上是手动延寿。
最后,std::begin
上面代码中的使用最好在返回值中允许 ADL 的上下文中完成。像这样创建一个辅助命名空间:
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
}
然后将我上面代码std::begin
中的s 替换为,它模拟了循环如何找到和更好地触摸(不完美,但更好)。decltype
adl_helper::adl_begin
for( a:b )
begin
end
C++1y 可能带有一些机制来消除对上述 hack 的需要。
运行示例代码:http: //ideone.com/RYvzD0