简体   繁体   English

c++ integer 的幂,模板元编程

[英]c++ power of integer, template meta programming

I want to make a function which returns a power of integer. Please read the fmuecke's solution in power of an integer in c++ .我想制作一个返回 integer 的幂的 function。请阅读 c++ 中 integer的 fmuecke 解决方案。

However, I want to generalize his solution to the arbitrary type T. Since c++11 has constexpr, I guess this is possible.但是,我想将他的解决方案推广到任意类型 T。由于 c++11 具有 constexpr,我想这是可能的。

Naively, I tried something like,天真地,我尝试了类似的东西,

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

Actually this approach failed since the partial specialization for function template is not allowed.实际上这种方法失败了,因为 function 模板的部分特化是不允许的。

And one more question.还有一个问题。 I heard that it is up to the compiler whether the constexpr function is evaluated in compile time or not.我听说 constexpr function 是否在编译时求值取决于编译器。 How do I force it to compute for general type.我如何强制它计算一般类型。 I read from somewhere that one of the simplest hack for integral consts is to wrap it in std::integral_const::value.我从某处读到,积分常量最简单的破解方法之一是将其包装在 std::integral_const::value 中。

Solution using recursion: 使用递归的解决方案:

#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 suggested/requested a version using exponentiation by squaring: 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);
}

"I heard that it is up to the compiler whether the constexpr function is evaluated in compile time or not." “我听说编译器是否在编译时评估constexpr函数是由编译器决定的。”

True, AFAIK. 是的,AFAIK。 The compiler isn't required to do constant-initialization at compile-time, but if you use the result of a constexpr function as a non-type template argument, it has to compute the result at compile-time. 编译器不需要在编译时进行常量初始化,但如果使用constexpr函数的结果作为非类型模板参数,则必须在编译时计算结果。

std::cout << std::integral_constant<int, pow(2, 4)>::value << std::endl;

Also see the approach using integral_constant as parameter of pow in Andy Prowl 's answer. 另请参阅Andy Prowl的回答中使用integral_constant作为pow参数的方法。

Here's how you can enforce compile-time evaluation: 以下是如何强制执行编译时评估:

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

Please leave a comment if you downvote so I can improve my answer. 如果你是downvote请发表评论,以便我可以改进我的答案。

When you find yourself in need of partially specializing a function template (beware, this does not mean that in this case you are in need, as DyP's answer shows), you may either resort to overloading (see the last update at the end of this answer) or, if that's not possible, wrap that function template into a class template, and have a static, non-template member function replace your original function template (and its specializations): 当你发现自己需要部分专门化一个功能模板时(注意,这并不意味着在这种情况下你需要,如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;
        }
    };
}

Then, you could provide a helper function template that delegates to the specialization of your helper class template: 然后,您可以提供一个辅助函数模板,该模板委托给您的帮助器类模板的特化:

template<int N, class T>
T constexpr pow(T const x)
{
    return detail::helper<T, N>::pow(x);
}

Here is a live example . 这是一个实例

EDIT: 编辑:

Notice, that the specialization for N == 1 is actually not necessary. 请注意, N == 1的特化实际上不是必需的。 I kept it in the original text because the purpose of this answer was mainly to show how to workaround the impossibility of partially specializing function templates in general - so I translated the original program piece-by-piece. 我一直是在原来的文本,因为这个答案的目的主要是为了展示如何解决办法的部分专业一般函数模板是不可能的-所以我翻译的原程序片段逐件。

As noted by Dyp in the comments , however, this would be enough: 正如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;
        }
    };
}

UPDATE: 更新:

As a further remark, please keep in mind that even when you can specialize function templates (eg with explicit - not partial - specializations), it is generally not a good idea to do so, because function template specialization does not normally behave as one would expect. 作为进一步的评论,请记住,即使您可以专门化功能模板(例如,使用显式 - 非部分 - 专业化),这通常不是一个好主意,因为功能模板专业化通常不像人们那样表现期望。

Most of those situations that may seem to ask for function template specialization can actually be achieved through overloading , powered by well-known techniques such as tag dispatching . 大多数可能似乎要求功能模板专业化的情况实际上可以通过重载实现,由标记调度等众所周知的技术提供支持。 An example is proposed by Potatoswatter in the comments , pointing out that std::integral_constant could be used in this situation: 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>());
}

However, all these guidelines on "how to solve problems that seem to require function template partial specialization" should be taken into consideration when they are really needed. 但是,当真正需要时,应该考虑所有这些关于“如何解决似乎需要功能模板部分专业化的问题”的指导原则。 In this concrete case, as DyP showed in his answer, they are not. 在这个具体的案例中,正如DyP在他的回答中所表明的那样,他们不是。

Here is a solution with a single function: 这是一个具有单一功能的解决方案:

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

Here is a simple solution: 这是一个简单的解决方案:

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

Clean and simple solution here:这里干净简单的解决方案:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM