15

I have the following very simple template. As I learned, ^ is not the exponential operator. Now I'm looking for a way to compute this power. There are many examples with a recursive template on the internet. This is not too difficult.

But I wonder: Is there actually no "built-in" method in C++ to compute this on compile time?

template <int DIM>
class BinIdx : Idx
{
        static const int SIZE = 3 ^ DIM; // whoops, this is NOT an exponential operator!
}
4

5 回答 5

12

As mentioned you can use << if the exponent is a power of two.

Otherwise, if the exponents are non-negative integers you can write a constexpr function like this one.

template<typename T, typename U>
auto constexpr pow(T base, U exponent) {
    static_assert(std::is_integral<U>(), "exponent must be integral");
    return exponent == 0 ? 1 : base * pow(base, exponent - 1);
}

This will obviously break for large exponents as well as negative ones, though.

I am not fully aware of how well compilers optimize function calls in constant expressions. Here's a manual optimization for cases where the exponents are powers of two. This will also reduce the amount of recursion done.

template<typename T>
bool constexpr is_power_of_two(T x) {
    return (x != 0) && ((x & (x - 1)) == 0);
}

template<typename T, typename U>
auto constexpr pow(T base, U exponent) {
    static_assert(std::is_integral<U>(), "exponent must be integral");
    if (is_power_of_two(exponent)) {
        return base << exponent;
    }
    return exponent == 0 ? 1 : base * pow(base, exponent - 1);
}

More efficient algorithms are also available. However, I am bad at computer science so I don't know how to implement them.

于 2014-12-03T11:34:12.820 回答
10

As an addition to elyse's answer, here is a version with recursion depth of log(n):

template<typename T>
constexpr T sqr(T a) {
    return a * a;
}

template<typename T>
constexpr T power(T a, std::size_t n) {
    return n == 0 ? 1 : sqr(power(a, n / 2)) * (n % 2 == 0 ?  1 : a);
}
于 2014-12-03T12:05:49.527 回答
9

You can use template metaprogramming. Let me show the code.

template <int A, int B>
struct get_power
{
    static const int value = A * get_power<A, B - 1>::value;
};
template <int A>
struct get_power<A, 0>
{
    static const int value = 1;
};

Usage:

std::cout << get_power<3, 3>::value << std::endl;

(live example)

于 2014-12-03T11:34:31.530 回答
5

No, there is no general built in way to calculate the power of values. There is the pow function from the standard library and you can use the << shift operator for the special case 2^x.

This would work in your case (*):

static const int SIZE = (1 << DIM);

* = You updated your question from 2^x to 3^x after I wrote my answer.

For another special case x^y where x and y are static, you can just write a long multiplication:

const result int = x*x*x*x*x;
于 2014-12-03T11:31:24.473 回答
2

A named operator library:

namespace named_operator {
  template<class D>struct make_operator{
    constexpr make_operator(){}
  };
  template<class T, char, class O> struct half_apply { T&& lhs; };

  template<class Lhs, class Op>
  constexpr
  half_apply<Lhs, '*', Op>
  operator*( Lhs&& lhs, make_operator<Op> ) {
    return {std::forward<Lhs>(lhs)};
  }

  template<class Lhs, class Op, class Rhs>
  constexpr auto
  times( Lhs&& lhs, Op, Rhs&& rhs, ... ) // ... keeps this the worst option
  -> decltype( invoke( std::declval<Lhs>(), Op{}, std::declval<Rhs>() ) )
  {
    // pure ADL call, usually based off the type Op:
    return invoke( std::forward<Lhs>(lhs), Op{}, std::forward<Rhs>(rhs)     );
  }

  template<class Lhs, class Op, class Rhs>
  constexpr auto
  operator*( half_apply<Lhs, '*', Op>&& lhs, Rhs&& rhs )
  -> decltype(
    times( std::declval<Lhs>(), Op{}, std::declval<Rhs>() )
  )
  {
    return times( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
  }
}

It only supports operator*, but extending it should be obvious. Picking names for times equivalents is a bit of an issue.

@Anton's solution, augmented with a named operator:

namespace power {
  template<typename T>
  constexpr T sqr(T a) {
    return a * a;
  }

  template<typename T>
  constexpr T power(T a, std::size_t n) {
    return n == 0 ? 1 : sqr(power(a, n / 2)) * (n % 2 == 0 ?  1 : a);
  }

  namespace details {
    struct pow_tag {};
    constexpr named_operator::make_operator<pow_tag> pow;

    template<class Scalar>
    constexpr Scalar times( Scalar lhs, pow_tag, std::size_t rhs ) {
      return power( std::forward<Scalar>(lhs), rhs );
    }
  }
  using details::pow;
}

and now this works:

using power::pow;
int array[ 2 *pow* 10 ] = {0};

live example.

于 2015-08-08T17:13:32.887 回答