c++ Unpacking parameter pack from template arguments
Solution 1
Recursive template solution:
// recursive helper struct
template <int n, int First, int ...Rest>
struct helper {
static void funcImpl(std::array<bool, SIZE>& temp) {
temp[First] = true;
helper<n - 1, Rest...>::funcImpl(temp);
}
};
// partial specialization to catch base case
template <int First>
struct helper<0, First> {
static void funcImpl(std::array<bool, SIZE>& temp) {
temp[First] = true;
}
};
template <int ...Args>
std::array<bool, SIZE> func() {
std::array<bool, SIZE> b = {}; // 0 inititalize array
helper<sizeof...(Args) - 1, Args...>::funcImpl(b);
return b;
}
EDIT: A super simplified version inspired by iavr's solution:
template <int... A>
std::array<bool, SIZE> func() {
std::array<bool, SIZE> b = {};
auto values = {A...};
std::for_each(values.begin(), values.end(), [&](int n){b[n] = true;});
return b;
}
Solution 2
Here is a much simpler solution that requires nothing extra, just this:
struct _do { template <typename... T> _do(T&&...) { } };
template <int... A>
std::array<bool, SIZE> func() {
std::array<bool, SIZE> b = {};
_do{b[A] = true...};
return b;
}
This assumes the array is first initialized and then populated. My previous solution computed all values at compile time and directly initialized the array with them. So this is probably faster to compile and slower to run.
Solution 3
Since C++17, you can use fold expressions:
template <std::size_t... ARGS>
constexpr std::array<bool, SIZE> func() {
std::array<bool, SIZE> b{};
((b[ARGS] = true), ...); // fold expression
return b;
}
For C++11 and 14, I'd prefer using an unnamed lambda, like this:
template <std::size_t... ARGS>
std::array<bool, SIZE> func() {
std::array<bool, SIZE> b{};
[](...){}(b[ARGS] = true...);
return b;
}
Solution 4
See live example.
Here's the implementation of func
:
template <int... A, int... N>
std::array<bool, sizeof...(N)>
func(sizes <N...>)
{
return std::array<bool, sizeof...(N)>{{in <N, A...>()...}};
}
template <int... A>
std::array<bool, SIZE>
func() { return func <A...>(range <SIZE>()); }
where sizes
represents an int
sequence, range <S>
constructs sequence 0,...,S-1
and in<N, A...>()
checks whether number N
is in sequence A...
(definitions in the live example).
This not the most efficient (compile-wise) way to implement, because for every element of N...
we need to scan pack A...
. It's better to scan packs A...
, L...
in parallel, with a modification of function in()
. But anyway this was more straightforward to think of and write down.
prestokeys
I'm an avid learner of C++ who was a total beginner back in 2013, and like to solve exercises that have not been solved anywhere before. Consequently, I am often musing over new questions or generalizing old questions. Usually, I can solve them on my own, but when I get stuck, or when the best solution I could come up with is sloppy or clearly much more complicated than necessary, I will find my way in stackoverflow. The experts here are so brilliant, and wish to join their ranks one day.
Updated on June 09, 2022Comments
-
prestokeys almost 2 years
How to achieve want I want below? The paramater pack I want to unpack is not in a function argument list but template argument list.
#include <iostream> #include <array> const std::size_t SIZE = 10; template <int...ARGS> std::array<bool, SIZE> func() { std::array<bool, SIZE> b; // I want to set b[n] = true, where n takes on all values from ARGS... // what to put in here??? return b; } // Example of what I want to achieve: int main() { const std::array<bool, SIZE> b = func<1,3,7>(); // I want b[1]==true, b[3]==true, b[7]==true, all others false for (int x: b) std::cout << x << std::endl; }
I have to use this form for func (instead of func(1,3,7)) to get my bigger program working (I'm dealing with multiple inheritance issues).
-
prestokeys about 10 yearsThis potentially most elegant solution sets the true values correct, but the remaining are not set to false. I initialized as you suggested: std::array<bool, SIZE> b = {false}; b = func<1,3,7>();
-
prestokeys about 10 yearsIt now works if change to std::array<bool, SIZE> b = {}; in func definition
-
iavr about 10 years@prestokeys I really thought that since
std::array
is an aggregate type, its elements are default-initialized. Anyhow, I've changed initialization tob = {};
, thanks. -
underscore_d about 8 yearsPerhaps not the fastest at compile-time, but the ability to initialise arrays in a
constexpr
way (which either this or a very similar pattern would do) can be invaluable. +1 -
e.doroskevic over 6 yearsThe last solution is simply beautiful.
-
Marek R about 2 yearsNice clean and works with C++11: godbolt.org/z/K4sKabz5v