шаблоны — нет ли встроенного способа для вычисления мощности во время компиляции в C ++?

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

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

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

12

Решение

Вы можете использовать шаблон метапрограммирования. Позвольте мне показать код.

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;

(живой пример)

5

Другие решения

Как уже упоминалось, вы можете использовать << если показатель степени является степенью двойки.

В противном случае, если показатели являются неотрицательными целыми числами, вы можете написать функцию 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);
}

Более эффективные алгоритмы также доступны. Однако я плохо разбираюсь в компьютерных науках, поэтому не знаю, как их реализовать.

9

В дополнение к ответ Элис, вот версия с глубиной рекурсии 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);
}
7

Нет, нет общего встроенного способа для вычисления силы значений. Здесь 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;
4

Библиотека именованных операторов:

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

живой пример.

1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector