Circular extention to std::array

69 Views Asked by At

I want to have an array like object of fixed size that loops and overwrites data as new data come in. I have defined this template class:

template <typename T, int N>
class CircularArray {
private:
  T data[N];
  std::size_t head = 0;
public:
  std::size_t size() const {
    return N;
  }
  T operator [](std::size_t i) const {
    std::size_t j = (i + head) % N;
    return data[j];
  };
  T & operator [](std::size_t i) {
    std::size_t j = (i + head) % N;
    return data[j];
  };
  void push(T val) {
    data[head] = val;
    head = (head + 1) % N;
  }
};

The idea is straightforward. The element at index 0 is the oldest element and the one at index N-1 is the newest element. This works alright for what I want, but I would like to have it behave like std::array. Is there an easy way to extend std::array to have this circular behaviour?

EDIT: I mean I would like to have all the extra functionality of std::array without having to explicitly define it from scratch. For example, to have an iterators that allows me to write C++17 style for loops using the using such objects.

1

There are 1 best solutions below

0
Chris On

You may wish to employ composition over inheritance for this type of scenario. Consider the following, where begin and end member functions are very easily implemented.

#include <array>
#include <algorithm>
#include <iostream>
#include <iterator>

template <typename T, std::size_t N>
class ArrayWrap {
    std::array<T, N> &arr;

public:
    using wrapped_arr_type = typename std::array<T, N>;
    using iterator = typename wrapped_arr_type::iterator;

    ArrayWrap(wrapped_arr_type &arr) : arr(arr) {}

    T operator[](std::size_t i) const {
        return arr[i % N];
    }

    T& operator[](std::size_t i) {
        return arr[i % N];
    }

    iterator begin() { return arr.begin(); }
    iterator end() { return arr.end(); }
};

int main() {
    std::array<int, 4> a { 1, 2, 3, 4 };
    ArrayWrap<int, 4> b(a);

    std::cout << b[6] << std::endl;

    for (auto x : b) {
        std::cout << x << std::endl;
    }

    std::copy(
        b.begin(), b.end(),
        std::ostream_iterator<int>(std::cout, "\n")
    );
}

The output:

3
1
2
3
4
1
2
3
4

Note also the use of std::size_t rather than int.