Reputation: 4677
I have a template class that contains a single member array of compile-time constant length. I want this array to be constant, but initializing it based on constructor-provided input is proving difficult:
struct Input {
int value;
};
template<size_t Size>
struct Foo {
int const myVals[Size];
Foo(std::array<Input, Size> const &in)
: myVals{ in[0].value, in[1].value, /* How many times? */ } {
}
}
Since I don't know the size of the array, I don't know how many values with which to initialize myVals
. The following code works, but I question whether it is the best approach:
template<size_t Size>
struct Foo {
std::array<int, Size> const myVals;
Foo(std::array<Input, Size> const &in)
: myVals{ toIntArray(in) } {
}
private:
static std::array<int, Size> toIntArray(std::array<Input, Size> const &in) {
std::array<int, Size> result;
for (size_t i{ 0 }; i < Size; ++i) {
result[i] = in[i].value;
}
return result;
}
}
Is there a more succinct, or generally more accepted way to populate the values of a constant member array?
Upvotes: 4
Views: 514
Reputation: 29193
You can use std::index_sequence
to get the indices of the array as a non-type template parameter pack. Then you can use a parameter pack expansion.
template<size_t Size>
struct Foo {
int const myVals[Size];
Foo(std::array<Input, Size> const &in)
: Foo(in, std::make_index_sequence<Size>()) { }
private:
template<size_t... Is>
Foo(std::array<Input, Size> const &in, std::index_sequence<Is...>)
: myVals{in[Is].value...} { }
}
Using a helper template with a size_t... Is
pack and a std::index_sequence<Is...>
argument is a common pattern for handling fixed-size indexable containers. The index_sequence
doesn't actually do anything; it's just a proxy so that the Is
template arguments can be deduced. E.g. if you check on Godbolt it appears to totally evaporate under -O1
.
Upvotes: 2