Search code examples
c++containers

std::vector of std::array of different sizes


As an exercise, I would like to construct a vector containing std::array<unsigned char, N> objects (where N varies).

My attempt was to construct a base class GenericArray from which a MyArray<N> will derive, such that the container will actually be: std::vector<GenericArray*>. However, since the actual array variable must reside in the derived class, I do not see a way to make use of this data from the std:vector<GenericArray*> itself.

Here is my full attempt, which obviously produces: error: ‘class GenericArray’ has no member named ‘data’

#include <array>
#include <cassert>
#include <iostream>
#include <vector>

template<std::size_t N>
using arr_t = std::array<unsigned char, N>;

class GenericArray
{
public:
    ~GenericArray() = default;
};

template<std::size_t N>
class MyArray : public GenericArray
{
public:
    arr_t<N> data;

    MyArray(const arr_t<N>& data)
    {
        this->data = data;
    }
};

int main(void)
{
    std::vector<GenericArray*> vs;

    vs.emplace_back(new MyArray<2>({ 'a', 'b' }));
    vs.emplace_back(new MyArray<4>({ 'A', 'B', 'C', 'D' }));

    assert(vs.size() == 2);

    for (const auto& x : vs[0]->data)
    {
        std::cout << x << "\n";
    }

    return 0;
}


Solution

  • You seem to be mixing two concepts. I recommend the version in eerorika's answer but if you really want base class pointers in your container, here's one way:

    #include <array>
    #include <iostream>
    #include <vector>
    #include <memory>
    
    class GenericArray {
    public:
        using value_type = unsigned char;
        using iterator = value_type*;
    
        template<std::size_t N>
        using arr_t = std::array<value_type, N>;
    
        virtual ~GenericArray() = default; // must be virtual to call the derived dtor
        virtual iterator begin() = 0;      // used for iterating in the derived class
        virtual iterator end() = 0;
        // add `const` versions too as needed
    };
    
    template<std::size_t N>
    class MyArray : public GenericArray {
    public:
        arr_t<N> data;
    
        MyArray(const arr_t<N>& data) : data(data) {}
        iterator begin() override { return data.data(); }             // overridden
        iterator end() override { return data.data() + data.size(); } // -"-
    };
    
    int main() {    // no need for main(void)
        std::vector<std::unique_ptr<GenericArray>> vs;
    
        vs.emplace_back(new MyArray<2>({ 'a', 'b' }));
        vs.emplace_back(new MyArray<4>({ 'A', 'B', 'C', 'D' }));
    
        // loop over the elements:
        for(auto& ptr : vs) {
            for(auto& x : *ptr) std::cout << x << ' ';
            std::cout << '\n';
        }
    }```