2014-12-03 1 views
11

У меня есть следующий очень простой шаблон. Как я узнал, ^ не является экспоненциальным оператором. Теперь я ищу способ вычислить эту власть. Существует много примеров с рекурсивным шаблоном в Интернете. Это не слишком сложно.Нет ли встроенного способа вычислить мощность во время компиляции на C++?

Но мне интересно: на самом деле нет встроенного метода в C++ для вычисления этого времени компиляции?

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

Если это всего лишь сила двух, используйте '1 << DIM'. В противном случае нет. – Zeta

+0

для полномочий двух ... '1 << DIM': p – melak47

+0

Это не обязательно сила 2 ;-) – Michael

ответ

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 явно запрашивает встроенную более прямую альтернативу метапрограммированию шаблонов. Так что это не ответ. –

+0

@ Cheersandhth.-Alf О, ты прав; Я не внимательно прочитал вопрос> o < – ikh

4

Нет, нет общего встроенного способа расчета значений. В стандартной библиотеке есть функция pow, и вы можете использовать оператор сдвига << для специального случая 2^x.

Это будет работать в вашем случае (*):

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

* = Вы обновили свой вопрос от 2^x к 3^x после того как я написал свой ответ.

Для другого частного случая х^у, где х и у являются статическими, вы можете просто написать длинное умножение:

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

Это, очевидно, будет ломаться и для крупных экспонентов, и для отрицательных.

Я не совсем понимаю, насколько хорошо компиляторы оптимизируют вызовы функций в постоянных выражениях. Вот ручная оптимизация для случаев, когда показатели имеют степень два. Это также уменьшит количество рекурсии.

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

+2

Ваш последний момент важен: легко заставить компилятор взорваться с вычислениями времени компиляции, если вы не будете осторожны. Это хорошая причина ** не ** предоставить что-то подобное в стандартной библиотеке –

+0

ICBW, но я думаю, что видел '' прототипы с надписью 'constexpr' в GCC – sehe

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

Именованная библиотека Оператор:

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.