Search code examples
c++stdarray

Make longer std::array accessible as if it's shorter


I am implementing my static multi-dimentional vector class. I am using std::array as the underlying data type.

template <typename T, std::size_t N>
class Vector {
    private:
    std::array<T, N> data;
};

I want to make my class downwards-compatible, so I am writing this:

template <typename T, std::size_t N>
class Vector : public Vector<T, N-1>{
    private:
    std::array<T, N> data;
};

template <typename T>
class Vector<T, 0> {};

My goal is that when one instance is used in downwards-compatible mode, its underlying data should be able to be reliably accessed:

template<typename T, std::size_t N>
T& Vector<T, N>::operator[](int i) {
    // Do boundary checking here
    return this->data[i];
}

void foo(Vector<int, 3>& arg) {
    arg[1] = 10;
}

Vector<int, 5> b;
foo(b);
// Now b[1] should be 10

There are two points here:

  • Vector<T, 5> should be accepted by foo(), Vector<T, 2> should be rejected.
  • Changes to b[0] through b[2] in foo() should pertain. b[3] and b[4] should not be accessible in foo().

How can I achieve that?


Solution

  • How about a simple read wrapper around std::array<> itself?

    template<typename T, std::size_t N>
    struct ArrayReader {
    public:
      // Intentionally implicit.
      template<std::size_t SRC_LEN>
      ArrayReader(std::array<T, SRC_LEN> const& src) 
      : data_(src.data()) {
        static_assert(SRC_LEN >= N);
      }
    private:
      T const* data_;
    };
    
    void foo(ArrayReader<float, 3>);
    void bar() {
        std::array<float, 4> a;
        std::array<float, 2> b;
    
        foo(a);
        foo(b); //BOOM!
    }
    

    Of course, you can easily substitute std::array for your own type, this is just an example of the principle.