0

In the following example, I need to initialize the std::array in the A::A(H h) constructor initializer list (because class H doesn't have a default constructor), but I can't do it with an initializer list since the array size is a template parameter.

Is there a way around this?

#include <array>
using namespace std;

struct Hash {
    const vector<int> &data;

    Hash(const vector<int> &data)
        : data(data) {
    }

    uint64_t operator()(int id) const {
        return data[id];
    }
};

template <class H, size_t N>
class A {
public:
    A(H h) {
    }

    std::array<H, N> hashes;
};
    

int main () {
    vector<int> data{1, 2, 3, 4};

    A<Hash, 4> a{Hash(data)};
}
greg_p
  • 315
  • 1
  • 8
  • 3
    How should these `Hash` objects be constructed, given that they can not be default-constructed? Put another way, what should be in the array? – Drew Dormann Jan 31 '22 at 19:17
  • Related to [why-does-stdarray-not-have-an-constructor-that-takes-a-value-for-the-array-to](https://stackoverflow.com/questions/17923683/why-does-stdarray-not-have-an-constructor-that-takes-a-value-for-the-array-to). – Jarod42 Jan 31 '22 at 19:47

1 Answers1

2

std::index_sequence was provided in order to simplify the metaprogramming task of creating and expanding a pack whose size is not fixed. Use std::make_index_sequence and delegate the construction to some private constructor that deduces the pack:

A(H h) : A(h, std::make_index_sequence<N>{}) {}

template <std::size_t... i>
A(H h, std::index_sequence<i...>) : hashes{((void)i, h)...} {}

Here, ((void)i, h) is a comma expression that evaluates to h, but since we mentioned the pack i inside it, it's eligible to be expanded using .... The result is N copies of h (one for each element of i). This expansion occurs inside a braced-init-list, so the result is a braced-init-list contaning N copies of h, which will then initialize the hashes member.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
Brian Bi
  • 111,498
  • 10
  • 176
  • 312