Reputation: 739
I am struggling with the creation of an object, because i have to create a new parameter pack
from an array inside a function. The array contains a few enum
elements of the same enum
type
.
The error message states, that the array is not static, thus not a valid input that could be converted into a parameter pack.
Is there a possibility to something as follows:
enum Enums{e1, e2};
template<typename T, auto ...Enums>
class Example{};
template<int size>
constexpr auto createObj(){
Enums enum_array[size] = {};
for(int i = 0; i< size ; i++){
//fill array with e1 and e2
}
return Example<double, enum_array>();
}
Error message:
error: the address of ‘enum_array’ is not a valid template argument because it does not have static storage duration
return Example<double, enum_array>();
Upvotes: 2
Views: 701
Reputation: 41780
You'll probably need a few indirection level. First, to use any value as template parameter, it must be a compile time constant for the compiler to instanciate the template and generate its code.
For that, you'll need to put the function calculating the array in a constexpr function:
template<int size>
constexpr auto createObjArray() {
auto enum_array = std::array<Enums, size>{};
for(int i = 0; i < size ; i++){
//fill array with e1 and e2
}
return enum_array;
}
Then, you'll need a way to expand the array into a pack. Right now in C++17 (and C++20) it cannot be done locally. You'll need an index sequence of some sort. The most straightforward way to do that is to use an helper function:
template<std::size_t... S>
constexpr auto createObjHelper(std::index_sequence<S...>){
constexpr auto enumArray = createObjArray<sizeof...(S)>();
return Example<double, enumArray[S]...>();
}
Then wrap all that from a single user facing function:
template<int size>
constexpr auto createObj(){
return createObjHelper(std::make_index_sequence<size>());
}
Note that many of those helper function can be achieved using lambdas. For example, a lambda can have a constexpr body and be executed at compile time inside a runtime function:
template<std::size_t... S>
constexpr auto createObjHelper(std::index_sequence<S...>){
// implicitly constexpr ---v
constexpr auto enumArray = []{
auto enum_array = std::array<Enums, size>{};
for(int i = 0; i < size ; i++){
//fill array with e1 and e2
}
return enum_array;
}();
return Example<double, enumArray[S]...>();
}
And In C++20, you can use familiar template function syntax for lambda to avoid the helper function:
template<int size>
constexpr auto createObj(){
// implicitly constexpr ---v
constexpr auto enumArray = []{
Enums enum_array[size] = {};
for(int i = 0; i < size ; i++){
//fill array with e1 and e2
}
return enum_array;
}();
return []<std::size_t... S>(std::index_sequence<S...>) -> decltype(auto) {
return Example<double, enumArray[S]...>();
}(std::make_index_sequence<size>());
}
In C++23, if structured binding pack introduction is approved, then the second lambda can be removed in favor of creating a new pack directly in the function:
template<int size>
constexpr auto createObj(){
// implicitly constexpr ---v
constexpr auto enumArray = []{
Enums enum_array[size] = {};
for(int i = 0; i < size ; i++){
//fill array with e1 and e2
}
return enum_array;
}();
constexpr auto&& [...allEnums] = enumArray;
return Example<double, allEnums...>();
}
Upvotes: 1