2014-12-03 23 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

相關問題