【问题标题】:Reverse iterators for C arraysC 数组的反向迭代器
【发布时间】:2017-10-17 15:18:40
【问题描述】:

对于使用 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 之类的函数中——上面的 sn-p 不会与 std::distance(x,b) (或类似的函数,大概)一起编译,但是需要手册(int*)演员。

【问题讨论】:

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


【解决方案1】:

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

因为C++14rbegin()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, ", "));

【讨论】:

  • 嗯,显然这是在您回答后出现在主页上,在问题发布两年后......但后来我写了自己的答案。没有冒犯的意思! :-P
【解决方案2】:

使用span 来包装您的数组,不要费心手动滚动您建议的那种代码。

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

跨度在 C++20 中已经进入标准,但是您询问了 C++11,因此您可以使用 C++ 指南支持库的跨度,例如来自GSL-lite implementation,它支持 C++11(微软的 GSL 不支持)。

Span 是引用类型并且非常轻量级 - 并且通常只是被优化掉了。因此,使用跨度通常不会花费您任何费用。

在您的示例中,您可以使用 span 重写代码,如下所示:

#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 中尚未标准化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-27
    • 2019-11-20
    • 2020-11-06
    • 2019-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多