代码之家  ›  专栏  ›  技术社区  ›  xinaiz

计算编译时数组时发生编译器相关错误

  •  1
  • xinaiz  · 技术社区  · 8 年前

    我的目标是在编译时计算阶乘数组,而不创建任何类对象或调用静态函数。以下是最基本的代码:

    #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无法计算?

    2 回复  |  直到 8 年前
        1
  •  3
  •   Johan Lundberg    8 年前

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

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

    因为 value 要设置为1的目标在基类中声明 Factorial<N,0> 但被隐藏在 价值 成员。

    顺便说一句,如果您确实想用这样的直接方法计算阶乘编译时间,那么您可以用C++17来实现这一点:

    template<size_t N>
    constexpr auto factorial(){
       if constexpr(N<2){
         return 1;
       }
       return N*factorial<N-1>();
    }
    
        2
  •  1
  •   Community holdenweb    7 年前

    我不知道你两个问题的答案,但我知道如何修复你的代码,以便它能在我可以方便地测试的所有编译器中工作,基于来自 this old answer this 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;
    }
    

    2. )编译时计算,除非编译器足够聪明,能够记忆 Factorial<I>::value 很可能是这样。

    比我更精通C++11特性的人可能能够简化这一点,尤其是 FactorialArray ,在编译器停止对我的抱怨之前,这是一种非常强烈的货物崇拜+改变。

    for (auto x : f) 与一起工作 FactorialArray(工厂阵列) ; 上面显示的更复杂的循环是为了证明索引正确。

    请注意 FactorialArray<10, 42> f; 将无投诉地编译,并错误地报告11!=42.在供公众使用的图书馆中,可能值得保存递归 FactorialArray(工厂阵列) 在一个 detail 名称空间,然后将其包装在不带变量参数的公共模板中:

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

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