2014-12-03 77 views
11

我有以下非常简单的模板。据我所知,^不是指数运算符。现在我正在寻找一种计算这种能力的方法。在互联网上有很多递归模板的例子。这并不难。在编译时C++中没有内置的方法来计算功耗吗?

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

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

如果它只是2的幂,则使用'1 << DIM'。否则,不。 – Zeta 2014-12-03 11:27:46

+0

对于两个功能......'1 << DIM':p – melak47 2014-12-03 11:27:49

+0

它不一定是2的幂;-) – Michael 2014-12-03 11:30:57

回答

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)

+1

OP明确要求模板元编程的内置更直接替代方案。所以这不是一个答案。 – 2014-12-03 11:37:44

+0

@ Cheersandhth.-Alf哦,你说得对;我没有仔细阅读这个问题> o < – ikh 2014-12-03 11:39:32

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

如前所述,您可以使用<<如果指数是两个电源。

否则,如果指数是非负整数,您可以编写一个像这样的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); 
} 

更高效的算法也是可用的。但是,我对计算机科学不好,所以我不知道如何实现它们。

+2

我认为你的函数的名字应该改变 - 因为'std :: pow'> o < – ikh 2014-12-03 11:35:56

+2

你的最后一点很重要:如果你不小心,编译器计算会很容易使编译器爆炸。这是一个很好的理由**不**在标准库 – 2014-12-03 11:36:54

+0

ICBW中提供类似的东西,但我想我已经在GCC中看到了''标记'constexpr'的原型 – sehe 2014-12-03 11:37:53

4

作为除了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); 
} 
1

一个名为operator库:

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当量的名字是有点问题。

@安东的解决方案,一个叫运营商增强:

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

相关问题