反转C数组的迭代器

时间:2017-10-17 15:18:41

标签: c++ c++11 stl iterator

对于使用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*)演员。

2 个答案:

答案 0 :(得分:2)

(我正在将评论变成帮助他人的答案。感谢chrisStoryTeller。)

C++14起,rbegin()rend()就像您所描述的一样。

还有一个adapter class可以将(正向)迭代器转换为反向迭代器。请注意,应将 forward begin()迭代器传递给make_reverse_iterator以使 reverse 迭代器生效,反之亦然:

    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, ", "));

答案 1 :(得分:1)

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

跨度是轻量级的包装程序,用于内存中连续的值序列,为您提供标准库容器在这些值上的所有“便利设施”,包括反向迭代器,循环范围等。

跨度已进入C ++ 20的标准,但是您询问了C ++ 11,因此可以使用C ++准则支持库的跨度,例如来自GSL-lite implementation的版本,该版本支持C ++ 11(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;    
}

和这个compiles(GodBolt)。

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