Question

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).

Was it helpful?

Solution

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;
}

OTHER TIPS

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.

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;
}

Demo


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;
}

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top