Search code examples
c++compile-time

C++ how to instantiate std::array inside a class constructor?


I want to have a big container (specifically a std::array) the size of which I know at compile time but which I can specify from the constructor.

I have been able to do the first part: i.e. a std::array which a size specified at compile time. Schematically:

// hpp
constexpr int my_size = 100;
class C
{
    std::array<int, my_size> data;
}
// main
c = C()

Now I would like to do the same thing, but with the std::array passed into the class constructor, possibly as an constexpr:

// hpp
class C
{
    std::array<int, mysize> data;
    C(constexpr int mysize);
}
// cpp
C::C(constexpr int mysize)
{
    data = std::array<int, mysize>
}
// main
constexpr int mysize = 100;
c = C(mysize);

Obviously this is wrong because mysize is passed in the constructor, but mysize is also part of the type which has to be known at the point of declaration.

So how do I do this?


Solution

  • Changing the extent (size) of std::array would change the size of the object containing it -- since the array elements are stored contiguously in the object rather than indirectly (e.g. via pointer).

    A class' type must be "complete" in order for a constructor to be called -- and for a class to be "complete", it must be fully defined (and thus, a size must already be known). So effectively, passing it in via the constructor is not possible -- even if the value is a true constant expression.

    The only way to accomplish what you are trying to do using std::array is to make the class a template on the size of the array. Templates allow the compiler to generate different classes based on the template argument inputs, so each instantiation can have a different size:

    template <std::size_t N>
    class C {
        ...
        std::array<int, N> data;
    };
    

    Note that in doing this, your definition for all associated functionality will likely be stuck in a header now -- since the entire definition must be visible for an instantiation to work correctly (which generally constrains them to headers). Additionally, templates can cause code-bloat by duplicating generated code for each instantiation; so this is something to consider if you anticipate a lot of instantiations of this template.


    If you want to ever use this type based on runtime inputs, it would likely be simpler to use std::vector instead.

    
    class C {
        C(int mysize) : data{mysize}{}
        std::vector<int> data;
    };
    

    However, the elements inside std::vector will not be stored directly in C, but rather will be indirectly stored in allocated memory (likely on the heap)