c++ power of integer,模板元编程

c++ power of integer, template meta programming

本文关键字:编程 power of integer c++      更新时间:2023-10-16

我想创建一个返回整数幂的函数。请读一读这个问题的解决方案c++中整数的幂。

然而,我想把他的解推广到任意类型T。因为c++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函数是否在编译时进行评估取决于编译器。我如何强制它为一般类型计算。我从某个地方读到,整型const最简单的方法之一是将其封装在std::integral_const::value中。

递归解:

#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建议/请求一个使用平方求幂的版本:

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;

参见Andy Prowl回答中使用integral_constant作为pow参数的方法。

下面是如何执行编译时求值:
#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;
}

如果你不喜欢,请留下评论,这样我可以改进我的答案。

当您发现自己需要部分专门化函数模板时(注意,这并不意味着在这种情况下您需要,正如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);
}

下面是一个的实例

编辑:

注意,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;
        }
    };
}

更新:

作为进一步的注释,请记住,即使可以专门化函数模板(例如,显式专门化-而不是部分专门化),这样做通常也不是一个好主意,因为函数模板专门化通常不会像人们期望的那样表现。

大多数看起来需要函数模板专门化的情况实际上可以通过重载来实现,这是由诸如标记调度等众所周知的技术提供支持的。Potatoswatter在评论中提出了一个例子,指出std::integral_constant可以用于这种情况:

template<class T>
inline constexpr T pow(const T x, std::integral_constant<int, 0>){
    return 1;
}
template<class T, int N>
inline constexpr T pow(const T x, std::integral_constant<int, N>){
    return pow(x, std::integral_constant<int, N-1>()) * x;
}
template<int N, class T>
inline constexpr T pow(const T x)
{
    return pow(x, std::integral_constant<int, N>());
}

然而,所有这些关于"如何解决似乎需要函数模板部分专门化"的问题的指导方针;在真正需要的时候应该加以考虑。在这个具体的例子中,正如DyP在他的回答中所表明的那样,它们不是。

用一个函数来解决这个问题:

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

有一个简单的解决方案:

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

简单明了的解决方案:

#include <cstddef>
template<size_t N, size_t P>
struct pow_constexpr { constexpr static auto value = N * pow_constexpr<N, P-1>::value; };
template<size_t N>
struct pow_constexpr<N, 1> { constexpr static auto value = N; };
template<size_t N>
struct pow_constexpr<N, 0> { constexpr static auto value = 1; };
int main() {
    return pow_constexpr<2, 30>::value; // 1073741824
}