Подтвердить что ты не робот

С++ power of integer, мета-программирование шаблонов

Я хочу сделать функцию, которая возвращает силу целого. Пожалуйста, прочитайте решение fmuecke в сила целого в С++.

Однако я хочу обобщить его решение на произвольный тип T. Поскольку С++ 11 имеет constexpr, я думаю, это возможно.

Наивно, я попробовал что-то вроде

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

На самом деле этот подход не удался, поскольку частичная специализация шаблона функции не разрешена.

И еще один вопрос. Я слышал, что до компилятора зависит, будет ли функция constexpr оцениваться во время компиляции или нет. Как заставить его вычислять общий тип. Я где-то читал, что один из самых простых взломов для интегральных consts заключается в том, чтобы обернуть его в std:: integral_const:: значение.

4b9b3361

Ответ 1

Решение с использованием рекурсии:

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

Джереми У. Мерфи предложил/запросил версию, используя возведение в степень, возведя квадрат:

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

"Я слышал, что компилятор зависит от того, выполняется ли функция constexpr во время компиляции или нет."

Правда, AFAIK. Компилятору не требуется выполнять постоянную инициализацию во время компиляции, но если вы используете результат функции constexpr как аргумент шаблона непигового типа, он должен вычислить результат во время компиляции.

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

Также см. подход, использующий integral_constant в качестве параметра pow в ответе Andy Prowl.

Здесь вы можете провести оценку времени компиляции:

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

Пожалуйста, оставьте комментарий, если вы делаете снизу, чтобы я мог улучшить свой ответ.

Ответ 2

Если вам требуется частично специализировать шаблон функции (будьте осторожны, это не означает, что в этом случае вы нуждаетесь, как показывает ответ DyP), вы можете либо прибегнуть к перегрузке (см. последнее обновление в конце этого ответа), либо, если это невозможно, оберните этот шаблон функции в шаблон класса и добавьте статическую функцию, отличную от шаблона, вместо исходного шаблона функции (и его специализация):

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

Затем вы можете предоставить шаблон вспомогательной функции, который делегирует специализацию вашего шаблона вспомогательного класса:

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

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

EDIT:

Обратите внимание, что специализация для N == 1 фактически не нужна. Я сохранил его в исходном тексте, потому что цель этого ответа состояла в основном в том, чтобы показать, как обходиться с невозможностью частично специализировать шаблоны функций вообще, поэтому я перевел исходную программу по частям.

Как отмечено Dyp в комментариях, этого было бы достаточно:

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:

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

Большинство из тех ситуаций, которые могут потребовать специализацию шаблона функций, могут быть фактически достигнуты путем перегрузки, основанной на хорошо известных методах, таких как отправка тегов. Пример предложен Potatoswatter в комментариях, указывая, что std::integral_constant может использоваться в этой ситуации:

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

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

Ответ 3

Вот решение с одной функцией:

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

Ответ 4

Вот простое решение:

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

Ответ 5

Другой простой способ

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

Время: log (b)