17

我想做一个返回整数幂的函数。请阅读 fmuecke 在 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 中。

4

4 回答 4

24

使用递归的解决方案:

#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_constantas parameter of的方法。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;
}

如果您投反对票,请发表评论,以便我改进我的答案。

于 2013-05-08T14:53:15.783 回答
19

当您发现自己需要部分专门化功能模板时(请注意,这并不意味着在这种情况下您需要,正如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请注意,实际上不需要专门化 for 。我将其保留在原始文本中,因为此答案的目的主要是说明如何解决通常无法部分专门化功能模板的问题-因此我逐段翻译了原始程序。

然而,正如 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<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 在他的回答中所表明的那样,事实并非如此。

于 2013-05-08T14:52:23.267 回答
5

这是一个具有单一功能的解决方案:

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);
}
于 2014-05-17T23:58:08.047 回答
0

这是一个简单的解决方案:

#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";
}
于 2017-05-08T10:46:24.810 回答