2016-07-22 102 views
1

我的目标是在编译时计算阶乘数组而不创建任何类对象或调用静态函数。下面是最少的代码:计算编译时数组时编译器相关的错误

#include <iostream> 
#include <cinttypes> 
#include <array> 

namespace CompileTime 
{ 
    enum {MaxFactorial = 10}; 

    template<size_t N, size_t I = N-1> 
    class Factorial : public Factorial<N, I-1> 
    { 
    public: 
     static const uint64_t value; 
    }; 


    template<size_t N> 
    class Factorial<N,1> : public Factorial<N, 0> 
    { 
    public: 
     static const uint64_t value; 
    }; 

    template<size_t N> 
    class Factorial<N,0> 
    { 
    public: 
     static const uint64_t value; 
     static std::array<uint64_t,N> array; 
    }; 


    template<size_t N> 
    const size_t Factorial<N,1>::value = Factorial<N,0>::array[1] = 1; 

    template<size_t N> 
    const size_t Factorial<N,0>::value = Factorial<N,0>::array[0] = 1; 

    template <size_t N, size_t I> 
    const size_t Factorial<N,I>::value = Factorial<N,0>::array[I] = 
            I * Factorial<N, I-1>::value; 

    template <size_t N> 
    std::array<uint64_t,N> Factorial<N, 0>::array; 

    template class Factorial<MaxFactorial>; 

    typedef Factorial<MaxFactorial> PrecomputerFactorial; 
} 

int main() 
{ 
    using CompileTime::PrecomputerFactorial; 

    for(auto x : PrecomputerFactorial::array) 
     std::cout << x << std::endl; 
    std::cout << std::endl; 
} 

与GCC 5.3.0编译提供计划输出:

0 
1 
2 
6 
24 
120 
720 
5040 
40320 
362880 

而且随着MSVC 2015年:

0 
1 
0 
0 
0 
0 
0 
0 
0 
0 

我有两个问题:一是 ,为什么array[0]具有价值0在这两种情况下,尽管被设置为1这里:

template<size_t N> 
    const size_t Factorial<N,0>::value = Factorial<N,0>::array[0] = 1; 

二,为什么MSVC 2015年无法计算呢?

+0

不能回答这两个问题的观察:对于GCC 6,计算阶乘并在静态构建时初始化数组,不在编译时。我想你可能需要在周围洒一些'constexpr'。 – zwol

回答

1

我不知道您的任一问题的答案,但我确实知道如何修复您的代码,以便它可以在我可以方便地测试的所有编译器中使用,这些编译器基于this old answerthis other old answer中的技术。我没有用MSVC测试过它,并且很想知道它是否工作。

#include <iostream> 
#include <cinttypes> 
#include <array> 

using std::uint64_t; 

// Helper template that computes the factorial of one integer 
template<uint64_t I> struct Factorial 
{ static constexpr uint64_t value = I * Factorial<I-1>::value; }; 

template<> struct Factorial<0> { static constexpr uint64_t value = 1; }; 

// FactorialArray recursively assembles the desired array as a variadic 
// template argument pack from a series of invocations of Factorial 
template<uint64_t I, uint64_t... Values> struct FactorialArray 
    : FactorialArray<I-1, Factorial<I>::value, Values...> 
{}; 

// and in the base case, initializes a std::array with that pack 
template<uint64_t... Values> struct FactorialArray<uint64_t(-1), Values...> 
    : std::array<uint64_t, sizeof...(Values)> 
{ 
    constexpr FactorialArray() 
    : std::array<uint64_t, sizeof...(Values)> ({{Values...}}) 
    {} 
}; 

int main() 
{ 
    static FactorialArray<10> f; 
    for (std::size_t i = 0; i < f.size(); i++) 
    std::cout << i << "! = " << f[i] << '\n'; 
    return 0; 
} 

为了概念上的简单,我从数组的汇编中分离了阶乘的计算。这意味着它需要O(N 2 )计算在编译时,除非编译器足够聪明来memoize的Factorial<I>::value,其很可能是。

有人比我更熟练地使用C++ 11功能,可能可以简化这一点,特别是FactorialArray的基本情况,这种情况非常困难+变化 - 直到编译器停止 - 抱怨对我而言。

for (auto x : f)确实与FactorialArray工作;上面显示的更复杂的循环是证明指数正确。

注意FactorialArray<10, 42> f;将编译无投诉,并会错误地报告说11! = 42。在用于公共消费的图书馆,它可能是值得squirrelling递归FactorialArray走在detail命名空间,那么在公共模板不采取可变参数包裹它:

namespace detail { 
    // Factorial and FactorialArray as above 
} 
template<uint64_t I> struct FactorialArray : detail::FactorialArray<I> {}; 

读者练习:更改此来计算two-argument Ackermann–Péter function,从而展示了模板元编程的图灵完备性以及如何构建二维数组。

+0

作业是否与GCC 5.3.0和MSVC 2015一起编译,除非不生成'0!'。正确的顺序应该是'1,1,2 ...',但它省略了第一个元素(但我看到'因子<0>'是专门的,嗯) – xinaiz

+0

我只注意到我自己。这是一个愚蠢的错误 - “FactorialArray”的基本情况应该是-1,而不是0.很快修复。 – zwol

+0

整齐而清晰:)但我想知道一件事 - “FactorialArray”的第一个非模板参数是“uint64_t”,那么是否可以用'-1'来专门化它,这将是'std :: numeric_limits :: MAX();'? – xinaiz

3

的一个基本问题是,使用数组下标符(array[0]),为constexpr操作。但它不是constexpr直到C++ 17: http://en.cppreference.com/w/cpp/container/array/operator_at

为什么数组[0]已值为0在两种情况下,尽管被设置为1 ...?

由于value你的目标是设定为1的基类Factorial<N,0>声明,而是由value构件在(基础案例模板)隐藏派生的类。

顺便说一句,如果你想计算阶乘编译时有这样一个直接的方法,你就可以用做这个C++ 17:

template<size_t N> 
constexpr auto factorial(){ 
    if constexpr(N<2){ 
    return 1; 
    } 
    return N*factorial<N-1>(); 
} 
+0

现在还不清楚为什么'Factorial '的array [0]和value不会被设置为'1'。我的意思是,它在被定义的那一刻被初始化了,对吧?它是基类成员初始化,不是派生的,对吗? – xinaiz