2013-05-08 14 views
15

Chcę utworzyć funkcję, która zwraca moc liczby całkowitej. Proszę przeczytać rozwiązanie fmuecke w power of an integer in c++.C++ potęga liczby całkowitej, programowanie meta szablonów

Jednak chcę uogólnić jego rozwiązanie do arbitralnego typu T. Ponieważ C++ 11 ma constexpr, myślę, że to jest możliwe.

Naiwnie, próbowałem coś podobnego,

template<class T, int N> 
inline constexpr T pow(const T x){ 
    return pow<N-1>(x) * x; 
} 
template<class T> 
inline constexpr T pow<T, 1>(const T x){ 
    return x; 
} 
template<class T> 
inline constexpr T pow<T, 0>(const T x){ 
    return 1; 
} 

Właściwie to podejście nie powiodło się, ponieważ częściowa specjalizacja szablonu dla funkcji nie jest dozwolone.

Jeszcze jedno pytanie. Słyszałem, że to od kompilatora zależy czy funkcja constexpr jest oceniana w czasie kompilacji czy nie. Jak wymusić na obliczeniu typ ogólny. Czytałem skądś, że jednym z najprostszych hack dla integralnych const jest opakować go w std :: integral_const :: value.

Odpowiedz

22

Rozwiązanie z użyciem rekurencji:

#include <iostream> 

template<class T> 
inline constexpr T pow(const T base, unsigned const exponent) 
{ 
    // (parentheses not required in next line) 
    return (exponent == 0) ? 1 : (base * pow(base, exponent-1)); 
} 

int main() 
{ 
    std::cout << "pow(2, 4): " << pow(2, 4) << std::endl; 
    std::cout << "pow(5, 0): " << pow(5, 0) << std::endl; 
} 

Jeremy W. Murphy zasugerował/o wersję używając algorytm szybkiego potęgowania:

template<class T> 
inline constexpr T pow(const T base, unsigned const exponent) 
{ 
    // (parentheses not required in next line) 
    return (exponent == 0)  ? 1 : 
      (exponent % 2 == 0) ? pow(base, exponent/2)*pow(base, exponent/2) : 
      base * pow(base, (exponent-1)/2) * pow(base, (exponent-1)/2); 
} 

„Słyszałem, że to zależy od kompilatora, czy funkcja constexpr jest oceniany w czasie kompilacji lub nie. "

Prawda, AFAIK. Kompilator nie musi wykonywać stałej inicjalizacji w czasie kompilacji, ale jeśli użyjesz wyniku funkcji constexpr jako argumentu szablonu innego niż typ, musi obliczyć wynik w czasie kompilacji.

std::cout << std::integral_constant<int, pow(2, 4)>::value << std::endl; 

zobaczyć również podejście z wykorzystaniem integral_constant jako parametr pow w odpowiedzi Andy Prowl „s.

Oto jak można wymusić ocenę kompilacji:

#include <iostream> 
#include <type_traits> 

// insert a constexpr `pow` implementation, e.g. the one from above 

template < typename T, T base, unsigned exponent > 
using pow_ = std::integral_constant < T, pow(base, exponent) >; 

// macro == error prone, you have been warned 
#define POW(BASE, EXPONENT) (pow_ < decltype(BASE), BASE, EXPONENT > :: value) 

int main() 
{ 
    std::cout << "pow(2, 4): " << pow_<int, 2, 4>::value << std::endl; 
    std::cout << "pow(2, 4): " << POW(2, 4) << std::endl; 
} 

proszę komentarz, jeśli downvote więc mogę poprawić moją odpowiedź odejść.

+1

czy jesteś pewien, że jest to szablon meta-programu? –

+3

@ BЈовић Nie, to nie, ale dlaczego potrzebujesz metaprogramowania szablonów? Możesz użyć funkcji 'constexpr' (i ich wyników) również w metaprogramowaniu. – dyp

+4

@ BЈовић: Poważnie, kogo to obchodzi? "Czy na pewno użyłeś niebieskiego młotka do tego paznokcia?" –

17

Kiedy znajdziesz się w potrzebie częściowo specjalizujący szablon funkcji (uwaga, to nie znaczy, że w tym przypadku jesteś w potrzebie, jak DyP's answer pokazach), można albo ośrodek przeciążenia (patrz ostatnia aktualizacja na końcu tej odpowiedzi) lub, jeżeli nie jest to możliwe, należy owinąć ten szablon funkcji do szablonu klasy i mieć statyczny, funkcję składową non-template zastąpić oryginalny szablon funkcji (i jego specjalizacji):

namespace detail 
{ 
    template<class T, int N> 
    struct helper 
    { 
     static constexpr T pow(const T x){ 
      return helper<T, N-1>::pow(x) * x; 
     } 
    }; 

    template<class T> 
    struct helper<T, 1> // Unnecessary specialization! (see the edit) 
    { 
     static constexpr T pow(const T x){ 
      return x; 
     } 
    }; 

    template<class T> 
    struct helper<T, 0> 
    { 
     static constexpr T pow(const T x){ 
      return 1; 
     } 
    }; 
} 

Następnie można podać szablon funkcji pomocnika, który deleguje do specjalizacji szablonu klasy pomocnika:

template<int N, class T> 
T constexpr pow(T const x) 
{ 
    return detail::helper<T, N>::pow(x); 
} 

Oto live example.

EDIT:

Zauważ, że specjalizacja dla N == 1 to faktycznie nie jest to konieczne. Zachowałem to w oryginalnym tekście, ponieważ celem tej odpowiedzi było przede wszystkim pokazanie, jak obejść niemożliwość częściowego wyspecjalizowania szablonów funkcji ogólnie - więc przetłumaczyłem oryginalny program kawałek po kawałku.

Jak zauważył DYP in the comments jednak, to byłoby na tyle:

namespace detail 
{ 
    template<class T, int N> 
    struct helper 
    { 
     static constexpr T pow(const T x){ 
      return helper<T, N-1>::pow(x) * x; 
     } 
    }; 

    template<class T> 
    struct helper<T, 0> 
    { 
     static constexpr T pow(const T x){ 
      return 1; 
     } 
    }; 
} 

UPDATE:

Jako kolejny uwaga, proszę pamiętać, że nawet jeśli może funkcję specjalizują szablony (np. z wyraźnymi - nie częściowymi - specjalizacjami), generalnie nie jest to dobry pomysł, ponieważ specjalizacja szablonu funkcji zwykle nie zachowuje się tak, jak można by oczekiwać.

Większość z tych sytuacji, które mogą wydawać się prosić o specjalizacji szablonu funkcja może zostać osiągnięte poprzez przeciążenia, napędzany przez dobrze znanych technik, takich jak tag wysyłki.Przykładem jest proponowane przez Potatoswatter in the comments, wskazując, że std::integral_constant mogłyby zostać wykorzystane w tej sytuacji:

template<class T> 
inline constexpr T pow(const T x, std::integral_constant<T, 0>){ 
    return 1; 
} 

template<class T, int N> 
inline constexpr T pow(const T x, std::integral_constant<T, N>){ 
    return pow(x, std::integral_constant<T, N-1>()) * x; 
} 

template<int N, class T> 
inline constexpr T pow(const T x) 
{ 
    return pow(x, std::integral_constant<T, N>()); 
} 

Jednak wszystkie te wytyczne na temat „jak rozwiązywać problemy, które wydają się wymagać szablonu funkcja częściową specjalizację” powinno być brane pod rozważyć, kiedy są naprawdę potrzebne. W tym konkretnym przypadku, jak wykazał DyP w swojej odpowiedzi, nie są.

+0

+1 Całkowicie podziwiam Cię, wydajesz się, że znasz bardzo trochę C++. Zawsze chętnie czytasz odpowiedzi. – taocp

+0

@taocp: Wielkie dzięki za twoje uznanie, ale celebrujesz mnie bardziej niż na to zasługuję;) Żałuję, że nie znam się na C++ –

+0

Czy wymagana jest częściowa specjalizacja dla 'N == 1'? – dyp

2

Oto rozwiązanie z jednej funkcji:

template <int N, class T> 
constexpr T pow(const T& x) 
{ 
    return N > 1 ? x*pow<(N-1)*(N > 1)>(x) 
       : N < 0 ? T(1)/pow<(-N)*(N < 0)>(x) 
         : N == 1 ? x 
            : T(1); 
} 
0

Oto proste rozwiązanie:

#include<bits/stdc++.h> 
using namespace std; 

template<int N, int M> 
struct Pow 
{ 
    enum { res = N * Pow<N,M-1>::res}; 
}; 


template<int N> 
struct Pow<N,0> 
{ 
    enum {res = 1}; 
}; 
int main() 
{ 
    cout<<Pow<2,3>::res<<"\n"; 
} 
0

Kolejny prosty sposób

template<class T> 
T Power(T a , T b) { 
    T x,y; 
    x = 1; 
    y = a; 
    while(b > 0) { 
    if(b&1) x = (x*y); 
    y = (y*y); 
    b >>= 1; 
    } 
return x; 
} 

Czas: log (b)

Powiązane problemy