programas - para que sirven los templates c++
potencia de c++ de entero, meta programaciĆ³n de plantillas (5)
Aquí hay una solución con una sola función:
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);
}
Quiero hacer una función que devuelva una potencia de número entero. Por favor, lea la solución de fmuecke en poder de un entero en c ++ .
Sin embargo, quiero generalizar su solución al tipo arbitrario T. Como c ++ 11 tiene constexpr, supongo que esto es posible.
Ingenuamente, intenté algo así como,
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;
}
En realidad, este enfoque falló, ya que la especialización parcial para la plantilla de función no está permitida.
Y una pregunta más. Escuché que depende del compilador si la función constexpr se evalúa en tiempo de compilación o no. ¿Cómo lo forzo a calcular por tipo general? Leí en alguna parte que uno de los ataques más simples para consteos integrales es envolverlo en std :: integral_const :: value.
Aquí hay una solución simple:
#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";
}
Cuando necesite especializar parcialmente una plantilla de función (tenga cuidado, esto no significa que en este caso lo necesite, como lo muestra la respuesta de DyP ), puede recurrir a la sobrecarga (consulte la última actualización al final de este documento). respuesta) o, si eso no es posible, envuelva esa plantilla de función en una plantilla de clase, y tenga una función estática, no miembro de plantilla, reemplace su plantilla de función original (y sus especializaciones):
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;
}
};
}
Luego, podría proporcionar una plantilla de función auxiliar que delegue en la especialización de su plantilla de clase auxiliar:
template<int N, class T>
T constexpr pow(T const x)
{
return detail::helper<T, N>::pow(x);
}
Aquí hay un ejemplo en vivo .
EDITAR:
Tenga en cuenta que la especialización para N == 1
realidad no es necesaria. Lo mantuve en el texto original porque el propósito de esta respuesta era principalmente mostrar cómo solucionar la imposibilidad de plantillas de funciones parcialmente especializadas en general , así que traduje el programa original pieza por pieza.
Como señaló Dyp en los comentarios , sin embargo, esto sería suficiente:
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;
}
};
}
ACTUALIZAR:
Como observación adicional, tenga en cuenta que incluso cuando puede especializar plantillas de funciones (por ejemplo, con especializaciones explícitas, no parciales), generalmente no es una buena idea hacerlo, porque la especialización de plantillas de funciones normalmente no se comporta como se haría. esperar.
La mayoría de las situaciones que parecen solicitar la especialización de plantillas de funciones se pueden lograr realmente mediante la sobrecarga , impulsada por técnicas bien conocidas como el despacho de etiquetas . Un ejemplo es propuesto por Potatoswatter en los comentarios , señalando que std::integral_constant
podría usarse en esta situación:
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>());
}
Sin embargo, todas estas directrices sobre "cómo resolver problemas que parecen requerir la especialización parcial de la plantilla de función" deben tenerse en cuenta cuando realmente se necesitan. En este caso concreto, como DyP mostró en su respuesta, no lo son.
Solución usando recursión:
#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 sugirió / solicitó una versión usando la exponenciación mediante cuadratura:
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);
}
"Escuché que depende del compilador si la función constexpr se evalúa en tiempo de compilación o no".
Es cierto, AFAIK. No es necesario que el compilador realice una inicialización constante en tiempo de compilación, pero si utiliza el resultado de una función constexpr como un argumento de plantilla sin tipo, tiene que calcular el resultado en tiempo de compilación.
std::cout << std::integral_constant<int, pow(2, 4)>::value << std::endl;
También vea el enfoque que usa integral_constant
como parámetro de pow
en la respuesta de Andy Prowl .
A continuación, le mostramos cómo puede aplicar la evaluación en tiempo de compilación:
#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;
}
Por favor, deja un comentario si bajas tu voto para que pueda mejorar mi respuesta.
Otra manera simple
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;
}
Tiempo: log (b)