0

Consider the following metafunction for an integral pow (it is just an example) :

class Meta
{
    template<int N, typename T> static constexpr T ipow(T x)
    {
        return (N > 0) ? (x*ipow<N-1>(x)) 
                       : ((N < 0) ? (static_cast<T>(1)/ipow<N>(x)) 
                                  : (1))
    }
};

How to write the stop condition for such a function ?

Vincent
  • 57,703
  • 61
  • 205
  • 388
  • 1
    @n.m. please post the solution how would you overload that ipow function template, I'm done banging my head :D – Mr.Anubis Sep 02 '12 at 15:44
  • With a nasty hack, I guess you could do `return (N > 0) ? (x * ipow<(N - 1) * (N > 0)>(x)) : ((N < 0) ? (static_cast(1)/ipow<-N * (N < 0)>(x)) : (1));`. That's really gross, so I don't recommend it. – Johannes Schaub - litb Sep 02 '12 at 16:21
  • @Mr.Anubis: JohannesSchaub-litb beat me to it, see his answer. – n. m. could be an AI Sep 02 '12 at 16:29
  • @JohannesSchaub-litb : Why does the original version causes infinite recursion and not your nasty hack (It works well, I tested it, but I don't understand exactly why) ? – Vincent Sep 02 '12 at 20:01
  • @Vincent because my nasty hack uses `<0>` as argument in the false branches, instead of ever-decrementing numbers. If you are at 0, the original version instantiates for -1, -2, ... without ending. The current (C++11) language does not prevent this recursion even if the condition of the branch is a compile time constant (as in your case). – Johannes Schaub - litb Sep 02 '12 at 20:11
  • @JohannesSchaub-litb : Ok, so in the original version, the compiler continue the recursions in the false branches even if it is useless. With your hack, you manually stop this useless recursion at 0, that's it ? – Vincent Sep 02 '12 at 20:15

3 Answers3

10

Anytime you ask yourself "how to simulate partial specialization for functions", you can think "overload, and let partial ordering decide what overload is more specialized".

template<int N>
using int_ = std::integral_constant<int, N>;

class Meta
{
    template<int N, typename T> static constexpr T ipow(T x)
    {
        return ipow<N, T>(x, int_<(N < 0) ? -1 : N>());
    }

    template<int N, typename T> static constexpr T ipow(T x, int_<-1>)
    {
        //                             (-N) ??
        return static_cast<T>(1) / ipow<-N>(x, int_<-N>());
    }

    template<int N, typename T> static constexpr T ipow(T x, int_<N>)
    {
        return x * ipow<N-1>(x, int_<N-1>());
    }

    template<int N, typename T> static constexpr T ipow(T x, int_<0>)
    {
        return 1;
    }
};

I think you wanted to pass -N instead of N at the comment-marked position.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • Why not simply remove the explicit template argument to `ipow`? – Xeo Sep 02 '12 at 16:18
  • @Xeo bah it becomes confusing then, because some of the overloads (the one for -1) still needs the "N" so it requires an explicit argument. I guess it is more helpful as it is. – Johannes Schaub - litb Sep 02 '12 at 16:30
  • Ah, I see. Yeah, since you're basically using the `N` for both direction and counting. – Xeo Sep 02 '12 at 16:31
5

A simple version might go like this:

template <typename T, unsigned int N> struct pow_class
{
    static constexpr T power(T n) { return n * pow_class<T, N - 1>::power(n); }
};

template <typename T> struct pow_class<T, 0>
{
    static constexpr T power(T) { return 1; }
};

template <unsigned int N, typename T> constexpr T static_power(T n)
{
    return pow_class<T, N>::power(n);
}

Usage:

auto p = static_power<5>(2);  // 32
ForEveR
  • 55,233
  • 2
  • 119
  • 133
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
2

Just use static members in a class template and specialize the class template. You might want to create a forwarding function template for convenience, though.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380