【问题标题】:c++ power of integer, template meta programmingc++ 整数的幂,模板元编程
【发布时间】:2013-05-02 20:39:46
【问题描述】:

我想做一个返回整数幂的函数。 请阅读 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 函数是否在编译时评估取决于编译器。 我如何强制它计算通用类型。 我从某处读到,积分 const 最简单的技巧之一是将其包装在 std::integral_const::value 中。

【问题讨论】:

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


    【解决方案1】:

    使用递归的解决方案:

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

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

    【讨论】:

    【解决方案2】:

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

    更新:

    进一步说明,请记住,即使您可以特化函数模板(例如,使用显式 - 而非部分 - 特化),这样做通常也不是一个好主意,因为函数模板特化通常不会像预期的那样表现。

    大多数似乎需要函数模板特化的情况实际上可以通过重载实现,由众所周知的技术(如标签调度)提供支持。 Potatoswatterin 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 在他的回答中所表明的那样,他们不是。

    【讨论】:

    • @taocp:非常感谢您的赞赏,但您对我的庆祝超出了我应得的程度;)我希望我了解 C++ 的每一点
    • @Sungmin:没关系,DyP 的回答显示了解决这个特殊问题的正确方法,我的主要是针对更一般情况的指导
    • @0x499602D2:确实如此 (7.1.5/2)
    • 真的需要constexpr 和inline 吗?
    • @0x499602D2:不,不是
    【解决方案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);
    }
    

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-11-11
        • 1970-01-01
        • 2015-12-03
        • 2013-09-14
        • 1970-01-01
        • 2021-02-20
        • 1970-01-01
        相关资源
        最近更新 更多