我正在尝试编写一个模板来计算编译期间的数字幂(我不是模板元编程专家,所以请多加评论)。下面是代码:
template<typename T, T X, uint64_t P>
struct Pow
{
static constexpr T result = X * Pow<T,X, P - 1>::result;
};
template<typename T, T X>
struct Pow<T, X, 0>
{
static constexpr T result = 1;
};
template<typename T, T X>
struct Pow<T, X, 1>
{
static constexpr T result = X;
};
我需要这样称呼:
Pow<decltype(4), 4, 2>::result
问题:是否可以编写帮助程序模板以使呼叫跳过decltype
?例如:
Pow<4, 2>::result
答案 0 :(得分:12)
从C ++ 17开始,您可以为auto
模板值使用X
类型
template <auto X, int64_t P>
struct Pow
{
static constexpr decltype(X) result = X * Pow<X, P - 1>::result;
};
template <auto X>
struct Pow<X, 0>
{
static constexpr decltype(X) result = 1;
};
您还可以看到,考虑到0
部分专门化,1
部分专门化是多余的(也是C ++ 11 / C ++ 14)。
在C ++ 17之前……我能想象得到的最好的方法是,避免显式使用T
类型,而是通过宏定义(通常不鼓励这样做,但是在这种情况下,我想可以合理)。
类似
#define PowMacro(X, P) Pow<decltype(X), X, P>
答案 1 :(得分:7)
确定可以跳过decltype,并且在使用C ++ 11 contexpr时不需要任何结构。例如:
#include <iostream>
#include <type_traits>
template<typename T, class = typename std::enable_if< std::is_arithmetic<T>::value >::type >
constexpr T pow(T n, T power) noexcept {
return power == 1 ? n : n * pow(n,power - 1);
}
int main(int argc, const char* argv) {
static_assert( 4 == pow(2,2) ,"wrong pow");
static_assert( 8.0F == pow(2.0F,3.0F) ,"wrong pow");
static_assert( 256.0 == pow(2.0,8.0) ,"wrong pow");
std::cout << "integer 2^2=" << pow(2, 2) << std::endl;
std::cout << "float 2^3=" << pow(2.0F, 3.0F) << std::endl;
std::cout << "double 2^8=" << pow(2.0, 8.0) << std::endl;
return 0;
}
P.S。 更快地获得力量的方法。实际代码应该使用类似的东西,因为编译时间也很重要。
#include <iostream>
#include <type_traits>
// https://en.wikipedia.org/wiki/Exponentiation_by_squaring
template<typename T>
constexpr T pow(const T base,const T power, typename std::enable_if< std::is_integral<T>::value >::type* = 0) {
return 1 == power
? base
: 0 == power
? 1
: (1 == (power & 1) )
? base * pow(base, power - 1)
: pow(base, (power >> 1) ) * pow( base, (power >> 1) );
}
#ifdef __GNUG__
// GCC able to use most of <cmath> at compile time, check <cmath> header
inline constexpr float pow(float base, float power) noexcept {
return __builtin_powf(base, power);
}
inline constexpr double pow(double base, double power) noexcept {
return __builtin_pow(base, power);
}
inline constexpr long double pow(long double base,long double power) noexcept {
return __builtin_powl(base, power);
}
#else
// slow
template<typename T>
constexpr T pow(T base, T power, typename std::enable_if< std::is_floating_point<T>::value >::type* = 0) noexcept {
return power == 1.0 ? base : base * pow(base,power - static_cast<T>(1.0) );
}
#endif // __GNUG__
int main(int argc, const char** argv) {
static_assert( 4 == pow(2,2) ,"wrong pow");
static_assert( 1024 == pow(2L,10L) ,"wrong pow");
static_assert( (1 << 20) == pow(2LL,20LL) ,"wrong pow");
std::cout << "integer 2^1=" << pow(2, 1) << std::endl;
std::cout << "integer 2^2=" << pow(2, 2) << std::endl;
std::cout << "long 2^10=" << pow(2L, 10L) << std::endl;
std::cout << "long long 2^20=" << pow(2LL, 20LL) << std::endl;
static_assert( 8.0F == pow(2.0F,3.0F) ,"wrong pow");
static_assert( 256.0 == pow(2.0,8.0) ,"wrong pow");
static_assert( 1024.0L == pow(2.0L,10.0L) ,"wrong pow");
std::cout << "float 2^3=" << pow(2.0F, 3.0F) << std::endl;
std::cout << "double 2^8=" << pow(2.0, 8.0) << std::endl;
std::cout << "long double 2^10=" << pow(2.0L, 10.0L) << std::endl;
return 0;
}