Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reverse iterators for C arrays

For traversing a C array using STL functions, the std::begin and std::end functions are quite handy equivalents of .begin() and .end(). However, there are no std::rbegin and std::rend equivalents of the reverse iterators for bidirectional C++ containers. Does such an equivalent exist under some other name, or is one easily made? I realize one difficulty is that std::begin generally returns a raw pointer, and for the reverse case this would need a wrapper so that the ++ operation could be overloaded. A very incomplete implementation might look like

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);
}

I tested this bare-bones implementation with the following code:

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;

Could this be fleshed out into a fully operational reverse-iterator class for C arrays, or will I run into further obstacles down the road? One possible roadblock seems to be implicit conversion to raw pointers, which I'd hoped would be used in functions like std::distance -- the above snippet won't compile with std::distance(x,b) (or similar functions, presumably) but needs the manual (int*) cast.

like image 391
jwimberley Avatar asked Oct 17 '17 15:10

jwimberley


People also ask

What are reverse iterators?

The reverse iterator adaptor iterates through the adapted iterator range in the opposite direction.

How do you iterate through a vector backwards?

The C++ function std::vector::rbegin() returns a reverse iterator which points to the last element of the vector. Reverse iterator iterates reverse order that is why incrementing them moves towards beginning of vector.

Can arrays use iterators C++?

They use iterators. An iterator is an object designed to traverse through a container (e.g. the values in an array, or the characters in a string), providing access to each element along the way.

What is Rbegin?

The rbegin() is a function in C++ STL. It returns a reverse iterator which points to the last element of the map. The reverse iterator iterates in reverse order and incrementing it means moving towards beginning of map.


2 Answers

(I'm turning the comments into an answer to help others. Credits to chris and StoryTeller.)

Since C++14 there are rbegin() and rend() just like you describe.

There is also an adapter class to convert a (forward) iterator into a reverse iterator. Please note the forward begin() iterator should be passed to make_reverse_iterator to make the reverse iterator, and vice versa:

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

like image 161
Pibben Avatar answered Oct 13 '22 14:10

Pibben


Use a span to wrap your array, and don't bother with hand-rolling the kind of code you suggest.

Spans are lightweight wrappers for contiguous sequences of values in memory, providing you with all the "amenities" of a standard library container over those values - including reverse iterators, ranged for loops and so on.

Spans have entered the standard in C++20, but you asked about C++11, so you can use the C++ guidelines support library's span, e.g. from the GSL-lite implementation, that supports C++11 (Microsoft's GSL doesn't).

Spans are reference-types and are extremely lightweight - and very often just get optimized away. So using a span often won't cost you anything.

In your example, with a span, you can rewrite the code as follows:

#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;    
}

and this compiles (GodBolt).

Note I used the rbegin() and rend() members rather than std::rbegin() and std::rend() since the latter are not yet standardized in C++11.

like image 28
einpoklum Avatar answered Oct 13 '22 13:10

einpoklum