2016-07-22 2 views
1

내 목표는 클래스 객체를 만들거나 정적 함수를 호출하지 않고 컴파일 타임에 계승의 배열을 계산하는 것입니다. 나는 두 가지 질문이컴파일 시간 배열을 컴파일 할 때 컴파일러 종속 오류

0 
1 
0 
0 
0 
0 
0 
0 
0 
0 

:

#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과 여기에 최소한의 코드는 먼저, 왜 array[0]1으로 설정되었지만 두 경우 모두 0 값을 갖습니다.

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

개념적 단순화를 위해 배열의 조립과 계승을 분리했습니다. 즉, 컴파일러가 Factorial<I>::value을 메모 할만큼 충분히 똑똑하지 않으면 컴파일 타임에 O (N)의 계산이 필요합니다.

C++ 11 기능을 사용하면 더 숙련 된 사람이 이것을 단순화 할 수 있습니다. 특히 컴파일러가 중지 할 때까지 많은화물 숭배 + 변경 사항이있는 FactorialArray의 기본 사례가 더 간단 할 수 있습니다 내 부분에.

for (auto x : f)FactorialArray과 호환됩니다. 위에 표시된 더 복잡한 루프는 인덱스가 올바르게 나오는지 보여주는 것입니다.

FactorialArray<10, 42> f;은 불만없이 컴파일되며 11을 잘못보고합니다. = 42.

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

운동 :이 변경 대중 소비를위한 도서관, 그것은 다음, detail 네임 스페이스에 떨어져 재귀 FactorialArray을 squirrelling 가변 인수를 사용하지 않는 공용 템플릿에 포장 가치가있을 수도 있습니다 two-argument Ackermann–Péter function을 계산하여 템플릿 메타 프로그래밍의 Turing-completeness와 2 차원 배열을 만드는 방법을 보여줍니다.

+0

GCC 5.3.0과 MSVC 2015 모두로 작업을 컴파일합니까? 단, '0!'은 생성되지 않습니다. 올바른 순서는'1,1,2 ...'이어야하지만, 첫 번째 요소는 생략합니다. (그러나 나는'Factorial <0>'이 특수화되어 있음을 알게됩니다.) – xinaiz

+0

방금 ​​저 자신이 알아 챘습니다. 어리석은 실수입니다. FactorialArray의 기본 경우는 0이 아닌 -1이되어야합니다. 곧 수정됩니다. – zwol

+0

깔끔하고 맑음 :)하지만 한가지 궁금한 점이 있습니다 -'FactorialArray'의 첫 번째 템플릿이 아닌 인수는'uint64_t'입니다.'-1'을 사용하여 그것을 전문화하는 것이 안전합니다. 이것은'std :: numeric_limits :: max();'? – xinaiz

3

근본적인 문제는 배열 첨자 연산자 (array[0])를 constexpr 연산으로 사용한다는 것입니다. 그러나 constexpr되지 17 C++ 때까지 어레이 [0]에도 불구하고 두 경우 모두에서의 값 0, 1로 설정되는 이유 http://en.cppreference.com/w/cpp/container/array/operator_at

...?

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