在C ++编译时没有内置的计算能力的方法吗?

时间:2014-12-03 11:24:59

标签: c++ templates compile-time-constant

我有以下非常简单的模板。据我所知,^不是指数运算符。现在我正在寻找一种计算这种能力的方法。互联网上有许多递归模板的例子。这并不太难。

但我想知道:实际上没有"内置" C ++中的方法在编译时计算它?

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

5 个答案:

答案 0 :(得分:9)

如上所述,如果指数是2的幂,则可以使用<<

否则,如果指数是非负整数,你可以像这样写一个constexpr函数。

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

但是,对于大型指数和负指数,这显然会中断。

我并不完全清楚编译器在常量表达式中如何优化函数调用。这是针对指数为2的幂的情况的手动优化。这也会减少递归的次数。

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

还提供更高效的算法。但是,我对计算机科学很擅长,所以我不知道如何实现它们。

答案 1 :(得分:7)

作为elyse's answer的补充,这是一个递归深度为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);
}

答案 2 :(得分:5)

您可以使用模板元编程。让我展示代码。

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

用法:

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

(live example)

答案 3 :(得分:4)

不,没有通用的内置方法来计算值的功效。标准库中有pow函数,您可以使用<<移位运算符来处理特殊情况2^x

这适用于您的情况(*):

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

* =在我写完答案后,您已将问题从2^x更新为3^x

对于另一个特殊情况x ^ y,其中x和y是静态的,你可以写一个长乘法:

const result int = x*x*x*x*x;

答案 4 :(得分:1)

命名的运算符库:

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

它只支持operator*,但扩展它应该是显而易见的。选择times等价物的名称是一个问题。

@Anton's的解决方案,使用命名运算符进行扩充:

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

现在可行:

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

live example