c ++整数的力量,模板元编程

时间:2013-05-08 14:45:00

标签: c++ templates c++11 template-meta-programming

我想创建一个返回整数幂的函数。 请阅读fmuecke的解决方案 power of an integer in 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函数是由编译器决定的。 如何强制它计算一般类型。 我从某处读到,对于整数consts来说,最简单的一个方法是将它包装在std :: integral_const :: value中。

4 个答案:

答案 0 :(得分:22)

使用递归的解决方案:

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

另请参阅使用integral_constant作为Andy Prowl答案中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;
}

如果你是downvote请发表评论,以便我可以改进我的答案。

答案 1 :(得分:18)

当你发现自己需要部分专门化功能模板时(注意,这并不意味着在这种情况下你需要,如DyP's answer所示),你可以求助于重载(请参阅本答案末尾的最后一次更新),或者,如果不可能,将该函数模板包装到类模板中,并使用静态非模板成员函数替换原始函数模板(及其特殊化) ):

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

这是live example

修改

请注意,N == 1的专业化实际上并不是必需的。我把它保留在原始文本中,因为这个答案的目的主要是为了说明如何解决部分特殊化功能模板的一般的不可能性 - 所以我逐个翻译了原始程序。

然而,如Dyp in the comments所述,这就足够了:

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 in the comments提出了一个例子,指出在这种情况下可以使用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在答案中所表明的那样,他们不是。

答案 2 :(得分: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);
}

答案 3 :(得分:0)

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

var mongoose=require('mongoose');
mongoose.Promise=global.Promise; 


module.exports=gamedb=mongoose.createConnection(config.database);