Search code examples
c++arraysc++14member-initializationindex-sequence

How to initialize a data member of an array type in a class template's member initializer list?


For example, I have a class called Vector that represents a vector and a class called Integer that represents an integer.

class Integer{
public:
   Integer(int v):value_(v){};
private:
   int value_;
};
template<uint32_t Dim>
class Vector{
public:
     Vector(int v[Dim]) 
      ://How to initialize each element of the value_ array here?
     {
     }
private:
     Integer value_[Dim];
}

Since Integer does not have a default constructor, you must initialize each element of the value_ array in the member initializer list of Vector. However, since the length of the array is a template parameter, you cannot directly use something like value_{v[0], v[1], v[2]} to initialize the value_ array. I'm using c++14.


Solution

  • As you've said, if the size was known, you could write value_{v[0], v[1], v[2]} directly.

    Solution A - std::index_sequence

    Given that the size isn't known, you can write something like:

    private:
    template <std::size_t... I>
    Vector(const int(&v)[N], std::index_sequence<I...>)
      : value_{v[I]...} {}
    
    public:
    Vector(const int(&v)[Dims])
      : Vector(v, std::make_index_sequence<Dims>{}) {}
    

    Note: this uses a reference to an array so that the size is checked.

    The pack expansion v[I]... acts like v[0], v[1], ... for an arbitrary amount Dims of indices (as specified when creating the index sequence with std::make_index_sequence.

    Solution B - aggregate initialization

    However, since your Vector class only contains an array member, it would be much easier to just have no constructor and rely on aggregate initialization instead. If you had no constructor, you could write Vector<3>{1, 2, 3} as well.

    Solution C - std::array

    You could also have a single constructor:

    // note: value_ should be std::array as well
    Vector(const std::array<int, Dims>& content) : value_{content} {}
    

    std::array has value semantics, and trivializes the problem because you can just copy/move it around like any other object.

    Further solutions

    See What is the idiomatic way to create a fixed size std::array from a fixed size std::span?. This question is about C++20, but the solutions there are applicable to older versions as well.