Search code examples
c++templatesc++20variadic-templates

Can I use `enable_if` on a non-type template parameter pack inside the class definition?


I would like to template a class on a possibly fixed number of bools, count them, and use that number as the template parameter to a base class. Something like this

template <int Dimensionality> struct Foo {}; // I want to inherit from this

// helpers to compile-time count the true values among bools
template <bool B, bool... Args>
struct count_true {
  static constexpr int value = count_true<B>::value + count_true<Args...>::value;
};

template <>
struct count_true<true> {
  static constexpr int value = 1;
};
template <>
struct count_true<false> {
  static constexpr int value = 0;
};

// I want something like this, pseudocode
template <bool... Args>
struct Bar : public Foo<count_true<Args...>::value> {
  Bar(bool... args) {
    // do something with the parameters
    static_assert(sizeof...(args) <= 1337);
  }
};

But I can't find a way to do this. There seem to be a lot of questions relating to the use of enable_if for parameter packs for functions, e.g.

Or uses where the template types are not used for inheritance, e.g.

But none of that has helped in this particular case. The "pseudocode" does not compile because bool... args is not an unexpanded parameter pack.

I was thinking something like this might work, but it does not, as the parameter pack must be the last template parameter:

template <class... Ts>
struct Bar : public Foo<count_true<Ts..., std::enable_if_t<(std::is_same<Ts, bool>::value && ...), bool>>::value> {
…
}

Is there a way to accomplish this? This question is probably a bit confused, as I really don't know what I'm doing w.r.t. template metaprogramming.


Solution

  • But none of that has helped in this particular case. The "pseudocode" does not compile because bool... args is not an unexpanded parameter pack.

    You can expand template parameters using decltype(). Additionally, count_true can be simply replaced with fold-expression.

    template <bool... Args>
    struct Bar : public Foo<(Args + ... + 0)> {
      Bar(decltype(Args)... args) {
        // do something with the parameters
        static_assert(sizeof...(args) <= 1337);
      }
    };