2014-12-03 10 views
11

Mam następujący bardzo prosty szablon. Jak się dowiedziałem, ^ nie jest operatorem wykładniczym. Teraz szukam sposobu na obliczenie tej mocy. Istnieje wiele przykładów z szablonem rekurencyjnym w Internecie. To nie jest trudne.Czy nie ma wbudowanego sposobu obliczania mocy podczas kompilacji w C++?

Ale zastanawiam się: czy faktycznie nie ma "wbudowanej" metody w C++, aby obliczyć to w czasie kompilacji?

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

Jeśli to tylko potęga dwóch, użyj '1 << DIM'. W przeciwnym razie nie. – Zeta

+0

dla mocy dwóch ... '1 << DIM': p – melak47

+0

To niekoniecznie potęga 2 ;-) – Michael

Odpowiedz

5

Można użyć metaprogramowania szablonu. Pozwól mi pokazać kod.

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

Zastosowanie:

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

(live example)

+1

OP wyraźnie żąda wbudowanej bardziej bezpośredniej alternatywy dla metaprogramowania szablonów. To nie jest odpowiedź. –

+0

@ Cheersandhth.-Alf Och, masz rację; Nie przeczytałem dokładnie tego pytania> o < – ikh

4

Nie, nie ma generalnego sposobu obliczania mocy wartości. Istnieje funkcja pow ze standardowej biblioteki i można użyć operatora zmiany << dla specjalnego przypadku 2^x.

ta będzie działać w Twoim przypadku (*):

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

* = Zaktualizowałeś pytanie od 2^x do 3^x po pisałem moją odpowiedź.

Na innym szczególnym przypadku x^y, gdzie x i y są statyczne, można po prostu napisać długą mnożenia:

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

Jak wspomniano można użyć << jeśli wykładnik jest potęgą dwójki.

W przeciwnym razie, jeśli wykładniki są nieujemnymi liczbami całkowitymi, można napisać funkcję constexpr, taką jak ta.

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

Oczywiście będzie to dotyczyć zarówno dużych wykładników, jak i negatywów.

Nie jestem w pełni świadomy tego, jak dobrze kompilatory optymalizują wywołania funkcji w stałych wyrażeniach. Oto ręczna optymalizacja dla przypadków, w których wykładniki są potęgami dwóch. Spowoduje to również zmniejszenie ilości wykonywanej rekursji.

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

Dostępne są również bardziej wydajne algorytmy. Jednak jestem zły w informatyce, więc nie wiem, jak je wdrożyć.

+2

Myślę, że nazwa funkcji powinna zostać zmieniona - z powodu 'std :: pow'> o < – ikh

+2

Twój ostatni punkt jest ważny: łatwo jest spowodować, że kompilator eksploduje podczas kompilacji, jeśli nie będziesz ostrożny. To jest dobry powód ** nie ** dostarczenia czegoś takiego w standardowej bibliotece –

+0

ICBW, ale myślę, że widziałem '' prototypy oznaczone "constexpr" w GCC – sehe

4

Jako dodatek do elyse's answer, tutaj jest wersja z głębokością rekurencji z 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

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

Obsługuje tylko operator*, ale jej przedłużenie powinno być oczywiste. Wyszukiwanie nazw dla odpowiedników times jest tylko pewnym problemem.

@ rozwiązania Anton, rozszerzonej o nazwie operatora:

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

a teraz to działa:

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

live example.