C数组的反向迭代器

Reverse iterators for C arrays

本文关键字:迭代器 数组      更新时间:2023-10-16

对于使用STL函数遍历C数组,std::beginstd::end函数是相当方便的.begin().end()等价函数。然而,对于双向C++容器,并没有std::rbeginstd::rend等效的反向迭代器。这样一个等价物是否以其他名称存在,或者很容易制作?我意识到一个困难是std::begin通常返回一个原始指针,而对于相反的情况,这将需要一个包装器,这样++操作可能会过载。一个非常不完整的实现可能看起来像

template<class T>
class ReverseArrayIterator {
public:
ReverseArrayIterator(T* ptr) : _ptr(ptr) {}
operator T*() {
return _ptr;
}
void operator++() {
--_ptr;
}
T operator*() {
return *_ptr;
}
bool operator!= (ReverseArrayIterator& rhs) {
return _ptr != rhs._ptr;
}
private:
T* _ptr;
};
template<class T, size_t size>
ReverseArrayIterator<T> rbegin(T (&array)[size]) {
return ReverseArrayIterator<T>(&array[0] + size - 1);
}
template<class T, size_t size>
ReverseArrayIterator<T> rend(T (&array)[size]) {
return ReverseArrayIterator<T>(&array[0] - 1);
}

我用以下代码测试了这个基本实现:

int x[] = {1,2,3,4,5,6,0,0,0,10,11,12};
auto a = std::find(std::begin(x),std::end(x),0);
auto b = std::find(rbegin(x),rend(x),0);
cout << std::distance(x,a) << endl;
cout << std::distance(x,(int*)b) << endl;

这能被充实成一个完全可操作的C数组反向迭代器类吗?或者我会遇到更多的障碍吗?一个可能的障碍似乎是对原始指针的隐式转换,我希望它能用于std::distance这样的函数中——上面的代码段不会使用std::distance(x,b)(或者类似的函数,可能)编译,但需要手动(int*)转换。

(我将这些评论转化为帮助他人的答案。感谢chris和StoryTeller。)

从C++14开始,就有rbgin()和rend(),正如您所描述的那样。

还有一个适配器类,用于将(正向)迭代器转换为反向迭代器。请注意,正向begin()迭代器应传递给make_reverse_iterator,以使反向迭代器,反之亦然:

std::vector<int> v{ 1, 3, 10, 8, 22 };
std::copy(
std::make_reverse_iterator(v.end()), 
std::make_reverse_iterator(v.begin()),
std::ostream_iterator<int>(std::cout, ", "));

使用span来包装您的数组,不必手动滚动您建议的代码。

Spans是内存中连续值序列的轻量级包装器,为您提供了标准库容器对这些值的所有"便利",包括反向迭代器、循环范围等

Spans已经成为C++20中的标准,但您询问了C++11,因此您可以使用C++指南支持库的span,例如从支持C++11的GSL lite实现(Microsoft的GSL没有)。

跨度是参考类型,非常轻,而且通常只是经过优化。因此,使用跨度通常不会花费任何费用。

在您的示例中,使用跨度,您可以按如下方式重写代码:

#include <iterator>
#include <gsl/gsl-lite.hpp>
#include <iostream>
int main()
{
int raw_x[] = {1, 2, 3, 4, 5, 6, 0, 0, 0, 10, 11, 12};
auto x = gsl::span<int>{raw_x};
auto a = std::find(x.begin(), x.end(),0);
auto b = std::find(x.rbegin(), x.rend(),0);
std::cout << std::distance(x.begin(), a) << std::endl;
std::cout << std::distance(x.rend(), b) << std::endl;    
}

并编译(GodBolt)。

注意,我使用了rbegin()rend()成员,而不是std::rbegin()std::rend(),因为后者尚未在C++11中标准化。