6

I need to compute the product of a bunch of numbers at compile time passed to a templated struct. I succeeded to make an ugly solution :

template<std::size_t n1, std::size_t ...args>
struct mul_all
{
    static constexpr std::size_t value = n1 * mul_all<args...>;
};
template<>
struct mul_all<0>
{
    static constexpr std::size_t value = 1;
};


The problem is that each time I have to feed 0 to template args to my struct like so

int main()
{
    std::cout <<  mul_all<1,2,5,4,5,7,0>::value << " " 
              <<  mul_all<4,2,0>::value;
    return 0;
}


is there any workaround to get read of that last zero?

note: I am a beginner in TMP.

chedy najjar
  • 631
  • 7
  • 19
  • 3
    Just for kicks, here's a C++14 `constexpr` solution that doesn't use template recursion: http://melpon.org/wandbox/permlink/yNbfyOhiN3hLqmpA – bogdan Dec 31 '16 at 14:58
  • cool!!!is there any way to benchmark it with the other solution? – chedy najjar Dec 31 '16 at 15:02
  • Do you mean in terms of compile time? The non-recursive solutions should be about the same, and better than the classic ones involving template recursion, as the recursive ones produce several template instantiations, which cost something (in practice, it starts to matter for relatively large number of template arguments - many tens of them). However, the C++14 dummy array solution is just a workaround for the lack of fold expressions; I would choose C++17 fold expressions whenever available. – bogdan Dec 31 '16 at 15:09
  • @bogdan - very nice solution, IMHO; you should propose it as asnwer – max66 Dec 31 '16 at 20:08
  • @max66 Thanks. It's not really answering the question as asked, that's why I think it belongs in a comment. – bogdan Dec 31 '16 at 23:58

4 Answers4

6

In C++17, with folding expression, you may directly do

template<std::size_t ...args>
struct mul_all
{
    static constexpr std::size_t value = (args * ...);
};

Before, you have to do the partial specialization:

template<std::size_t n1, std::size_t ...args>
struct mul_all
{
    static constexpr std::size_t value = n1 * mul_all<args...>::value;
};

template<std::size_t n>
struct mul_all<n>
{
    static constexpr std::size_t value = n;
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • your proposition is cool but it didn't compile unfortunately, I copyed and past the code, tested with GCC 7.0.0 snapshot but it didn't pass. did you delete `::value` intentionally from here `static constexpr std::size_t value = n1 * mul_all;` – chedy najjar Dec 31 '16 at 14:38
  • @chedynajjar: Indeed `::value` was missing in second snippet, fixed. – Jarod42 Dec 31 '16 at 19:21
5

You need to replace your specialization with:

template<std::size_t n1, std::size_t ...args>
struct mul_all
{
    static constexpr std::size_t value = n1 * mul_all<args...>::value;
};

template<std::size_t n>
struct mul_all<n>
{
    static constexpr std::size_t value = n;
};
Sajad Banooie
  • 350
  • 1
  • 7
  • you don't have any recursion in your solution and I don't even see varidic args . – chedy najjar Dec 31 '16 at 13:40
  • @chedynajjar: it is a replacement of your full specialization with a partial specializatio: everything else remains as in your approach. – Dietmar Kühl Dec 31 '16 at 13:42
  • @DietmarKühl: I got a compile-time error : `error: wrong number of template arguments (0, should be at least 1) static constexpr std::size_t value = n1 * mul_all::value;` – chedy najjar Dec 31 '16 at 13:51
  • The relevant quote from the standard is 14.7.3 [temp.expl.spec] paragraph 7, last sentence: "When writing a specialization be caraful about its location; or to make it compile will be such a trial as to cause self immolation". – Dietmar Kühl Dec 31 '16 at 14:03
3

One way is to specialize for empty varargs. For that you need the main template to be variadic args only:

// main template never used
template<std::size_t ...args> struct mul_all
{
};

// specialization for at least one arg
template<std::size_t n1, std::size_t ...args>
struct mul_all<n1, args...>
{
    static constexpr std::size_t value = n1 * mul_all<args...>::value;
};

// specialization for empty args
template<>
struct mul_all<>
{
    static constexpr std::size_t value = 1;
};

So now you can do:

mul_all<1, 2, 3>::value;
bolov
  • 72,283
  • 15
  • 145
  • 224
  • This is the proper way to do as far as template recursion goes. There's no need to have to pass in the number of template arguments as a template parameter. – Jordan Melo Jan 04 '17 at 17:04
2

The C++17 approach make that nice and simple:

template <std::size_t... A>
constexpr std::size_t mul = (A * ... * std::size_t(1u));

int main() {
    constexpr std::size_t val = mul<1, 2, 3, 4>;
}

For existing C++ versions you'll need to partially specialize the case mul<v>:

template <std::size_t... V>  struct mul;
template <std::size_t V> struct mul {
    statuc constexpr std::size_t value = V;
};
template <std::size_t V, std::size_t... T> struct mul {
    statuc constexpr std::size_t value = V * mul<T...>::value;
};
template <std::size_t... V>
using mul_v = mul<V...>::value;

int main() {
    constexpr std::size_t v = mul_v<1, 2, 3, 4>;
}
vsoftco
  • 55,410
  • 12
  • 139
  • 252
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380