计算编译时数组时编译器相关错误
Compiler dependent error when computing compile-time array
我的目标是在编译时计算阶乘数组,而不创建任何 class 对象或调用静态函数。这是最少的代码:
#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无法计算?
一个根本问题是您将数组下标运算符 (array[0]
) 用作 constexpr 运算。但在 C++17 之前它不是 constexpr:
http://en.cppreference.com/w/cpp/container/array/operator_at
Why array[0] has value 0 in both cases, despite being set to 1 ... ?
因为您打算设置为 1 的 value
是在基础 class Factorial<N,0>
中声明的,但被(基础案例模板中的 value
成员隐藏了) 导出 class.
顺便说一下,如果您确实喜欢使用像这样的直接方法计算阶乘编译时间,您将能够使用 C++17 执行此操作:
template<size_t N>
constexpr auto factorial(){
if constexpr(N<2){
return 1;
}
return N*factorial<N-1>();
}
我不知道你的任何一个问题的答案,但我知道如何修复你的代码,以便它在我可以方便地测试的所有编译器中工作,基于 this old answer and 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;
}
为了概念上的简单,我将阶乘的计算与数组的组装分开了。这意味着它在编译时需要 O(N2) 计算,除非编译器足够聪明以记住 Factorial<I>::value
,这很可能是。
比我更擅长 C++11 功能的人可能能够简化这个,特别是 FactorialArray
的基本情况,这是非常多的货物崇拜 + change-things-until-the-编译器停止抱怨我的部分。
for (auto x : f)
与 FactorialArray
一起工作;上面显示的更复杂的循环是为了证明索引是正确的。
注意FactorialArray<10, 42> f;
会毫无怨言地编译,会报错11! = 42. 在用于 public 消费的库中,可能值得将递归 FactorialArray
存放在 detail
命名空间中,然后将其包装在一个 public 模板中t 采用可变参数:
namespace detail {
// Factorial and FactorialArray as above
}
template<uint64_t I> struct FactorialArray : detail::FactorialArray<I> {};
reader 的练习:将此更改为计算 two-argument Ackermann–Péter function,从而演示模板元编程的图灵完备性以及如何构造二维数组。
我的目标是在编译时计算阶乘数组,而不创建任何 class 对象或调用静态函数。这是最少的代码:
#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无法计算?
一个根本问题是您将数组下标运算符 (array[0]
) 用作 constexpr 运算。但在 C++17 之前它不是 constexpr:
http://en.cppreference.com/w/cpp/container/array/operator_at
Why array[0] has value 0 in both cases, despite being set to 1 ... ?
因为您打算设置为 1 的 value
是在基础 class Factorial<N,0>
中声明的,但被(基础案例模板中的 value
成员隐藏了) 导出 class.
顺便说一下,如果您确实喜欢使用像这样的直接方法计算阶乘编译时间,您将能够使用 C++17 执行此操作:
template<size_t N>
constexpr auto factorial(){
if constexpr(N<2){
return 1;
}
return N*factorial<N-1>();
}
我不知道你的任何一个问题的答案,但我知道如何修复你的代码,以便它在我可以方便地测试的所有编译器中工作,基于 this old answer and 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;
}
为了概念上的简单,我将阶乘的计算与数组的组装分开了。这意味着它在编译时需要 O(N2) 计算,除非编译器足够聪明以记住 Factorial<I>::value
,这很可能是。
比我更擅长 C++11 功能的人可能能够简化这个,特别是 FactorialArray
的基本情况,这是非常多的货物崇拜 + change-things-until-the-编译器停止抱怨我的部分。
for (auto x : f)
与 FactorialArray
一起工作;上面显示的更复杂的循环是为了证明索引是正确的。
注意FactorialArray<10, 42> f;
会毫无怨言地编译,会报错11! = 42. 在用于 public 消费的库中,可能值得将递归 FactorialArray
存放在 detail
命名空间中,然后将其包装在一个 public 模板中t 采用可变参数:
namespace detail {
// Factorial and FactorialArray as above
}
template<uint64_t I> struct FactorialArray : detail::FactorialArray<I> {};
reader 的练习:将此更改为计算 two-argument Ackermann–Péter function,从而演示模板元编程的图灵完备性以及如何构造二维数组。