2014-12-03 21 views
11

Ich habe die folgende sehr einfache Vorlage. Wie ich gelernt habe, ist ^ nicht der Exponentialoperator. Jetzt suche ich nach einer Möglichkeit, diese Kraft zu berechnen. Es gibt viele Beispiele mit einer rekursiven Vorlage im Internet. Das ist nicht zu schwierig.Gibt es keine integrierte Möglichkeit, in C++ zur Kompilierzeit eine Leistung zu berechnen?

Aber ich frage mich: Gibt es tatsächlich keine "eingebaute" Methode in C++, um dies zur Kompilierzeit zu berechnen?

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

Wenn es nur eine Potenz von zwei ist, verwenden Sie '1 << DIM'. Ansonsten, nein. – Zeta

+0

für Potenzen von zwei ... '1 << DIM': p – melak47

+0

Es ist nicht unbedingt eine Potenz von 2 ;-) – Michael

Antwort

5

Sie können die Metaprogrammierung der Vorlage verwenden. Lass mich den Code zeigen.

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

Verbrauch:

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

(live example)

+1

Das OP fragt explizit nach einer integrierten direkteren Alternative zur Template-Metaprogrammierung. Das ist also keine Antwort. –

+0

@ Cheersandthth.-Alf Oh, du hast recht; Ich habe die Frage nicht sorgfältig gelesen> o < – ikh

4

Nein, es gibt keinen allgemeinen eingebauten Weg, um die Stärke von Werten zu berechnen. Es gibt die Funktion pow aus der Standardbibliothek, und Sie können den << Schichtoperator für den Sonderfall 2^x verwenden.

in Ihrem Fall wäre dies (*):

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

* = Sie aktualisiert Ihre Frage 2^x-3^x, nachdem ich meine Antwort geschrieben.

Für einen weiteren Spezialfall x^y, wobei x und y statisch sind, können Sie einfach eine lange Multiplikation schreiben:

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

Wie bereits erwähnt Sie << wenn der Exponent eine Zweierpotenz ist verwenden können.

Andernfalls, wenn die Exponenten nicht negative ganze Zahlen sind, können Sie eine constexpr-Funktion wie diese schreiben.

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

Dies wird natürlich für große Exponenten aber auch für negative aufbrechen.

Ich bin mir nicht völlig bewusst, wie gut Compiler Funktionsaufrufe in konstanten Ausdrücken optimieren. Hier ist eine manuelle Optimierung für Fälle, in denen die Exponenten Potenzen von zwei sind. Dies wird auch den Umfang der durchgeführten Rekursion reduzieren.

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

Effizientere Algorithmen sind ebenfalls verfügbar. Ich bin jedoch schlecht in der Informatik, daher weiß ich nicht, wie ich sie umsetzen soll.

+2

Ich denke, der Name Ihrer Funktion sollte geändert werden - wegen 'std :: pow'> o < – ikh

+2

Ihr letzter Punkt ist wichtig: Es ist einfach, den Compiler mit compile-time Berechnungen explodieren zu lassen, wenn Sie nicht vorsichtig sind. Das ist ein guter Grund ** nicht **, etwas wie das in der Standardbibliothek –

+0

ICBW zu liefern, aber ich glaube, ich habe ' 'Prototypen mit der Aufschrift' constexpr' in GCC – sehe

4

Als Ergänzung zu elyse's answer, hier ist eine Version mit Rekursionstiefe 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

Eine benannte Operator Bibliothek:

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

Es unterstützt nur operator*, aber erweitern sollte es offensichtlich sein. Kommissionierung Namen für times Äquivalente ist ein bisschen ein Problem.

@ Anton-Lösung, mit einem benannten Betreiber ergänzt:

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

und jetzt dies funktioniert:

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

live example.