2016-07-22 101 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