是否有更好的方法通过模板填充数组的预先计算值(在运行时使用)?

时间:2013-01-26 23:15:51

标签: c++ templates c-preprocessor

所以,假设我有一个模板结构 - 函数fib<i>::value。我想在运行时获得第n个斐波纳契数。为此,我创建数组fibs[] = { fib<0>::value, ... , fib<maxN>::value }。不幸的是,对于某些函数maxN可能非常大,我不能只用手填充它。因此,我编写了一些预处理程序指令,以使任务更容易。

#define fib(x) (fib<(x)>::value)
#define fibLine_level_0(x) fib(5*(x) + 0), fib(5*(x) + 1), fib(5*(x) + 2), fib(5*(x) + 3), fib(5*(x) + 4)
#define fibLine_level_1(x) fibLine_level_0(2*(x) + 0), fibLine_level_0(2*(x) + 1)
#define fibLine_level_2(x) fibLine_level_1(2*(x) + 0), fibLine_level_1(2*(x) + 1)
#define fibLine_level_3(x) fibLine_level_2(2*(x) + 0), fibLine_level_2(2*(x) + 1)

#define cAarrSize(x) (sizeof(x) / sizeof(x[0]))

我这样使用它:

int fibs[] = { fibLine_level_3(0) };

for (int i = 0; i < cAarrSize(fibs); i++)
    cout << "fib(" << i << ") = " << fibs[i] << endl;

您可能需要的代码:

template<int i>
struct fibPair{
    static const int fst = fibPair<i-1>::snd;
    static const int snd = fibPair<i-1>::fst + fibPair<i-1>::snd;
};

template<>
struct fibPair<0> {
    static const int fst = 0;
    static const int snd = 1;
};

template<int i>
struct fib {
    static const int value = fibPair<i>::fst;
};

但这段代码真的很难看。怎么做才能让它变得更漂亮?

约束:此代码必须用于运动编程。这意味着 - 没有第三方库,有时没有C ++ 11(但它可以)

2 个答案:

答案 0 :(得分:3)

Fib结构可以重写如下:

template <size_t i>
struct fib
{
    static const size_t value = fib<i - 1>::value + fib<i - 2>::value;
};

template <>
struct fib<0>
{
    static const size_t value = 0;
};

template <>
struct fib<1>
{
    static const size_t value = 1;
};

可以使用C ++ 11计算Fibonacci数的编译时数组。

修改1 (更改了fib值的类型)。

编辑2

Fibonacci数字数组的编译时生成(基于this答案)。

template<unsigned... args> struct ArrayHolder
{
    static const unsigned data[sizeof...(args)];
};

template<unsigned... args>
const unsigned ArrayHolder<args...>::data[sizeof...(args)] = { args... };

template<size_t N, template<size_t> class F, unsigned... args>
struct generate_array_impl
{
    typedef typename generate_array_impl<N-1, F, F<N>::value, args...>::result result;
};

template<template<size_t> class F, unsigned... args>
struct generate_array_impl<0, F, args...>
{
    typedef ArrayHolder<F<0>::value, args...> result;
};

template<size_t N, template<size_t> class F>
struct generate_array
{
    typedef typename generate_array_impl<N-1, F>::result result;
};

int main()
{
    const size_t count = 10;
    typedef generate_array<count, fib>::result fibs;

    for(size_t i = 0; i < count; ++i)
        std::cout << fibs::data[i] << std::endl;
}

您只需要为generate_array提供生成«功能»(我们的fib结构)。

答案 1 :(得分:0)

感谢@nameless,提供问题的链接,我在@MichaelAnderson找到了answer的简单c ++(没有新功能)。我使用它并根据自己的需要进行扩展。

所以,概念很简单,但有点奇怪。我们必须产生递归模板结构,其中第一个字段是与其他参数相同的模板结构。

template<size_t N>
struct FibList {
    FibList<N-1> previous;
    size_t value;
    FibList<N>() : value(fib<N>::value) {}
};

让我们尝试扩展一下(只是为了看看,编译器会产生什么):

template<size_t N>
struct FibList {
    FibList<N-3> previous;
    size_t value_N_minus_2;
    size_t value_N_minus_1;
    size_t value_N;
};

所以我们可以认为FibList是数组并且只是投射它(这是我的解决方案的弱点 - 我现在无法证明这一点)

static const size_t maxN = 2000;
FibList<maxN> fibList;
size_t *fibArray = &fibList.value - maxN;

或以另一种方式:

size_t *fibArray = reinterpret_cast<size_t*>(&fibList);

重要:数组的大小为maxN + 1,但获取数组大小(sizeof(array) / sizeof(array[0])的标准方法将失败。对此非常准确。

现在我们必须停止递归:

// start point
template<>
struct FibList<0> {
    size_t value;
    FibList<0>() : value(0) {}
};

// start point
template<>
struct FibList<1> {
    FibList<0> previous;
    size_t value;
    FibList<1>() : value(1) {}
};

注意,交换FibList<1>FibList<0>的位置会在编译器中产生堆栈溢出。

我们必须解决另一个问题 - 模板递归的深度有限(取决于编译器和/或选项)。但是,幸运的是,编译器只有深度限制,而不是模板的内存限制(好吧,是的,内存限制比深度限制更大)。因此,我们有明显丑陋的解决方案 - 串联调用fib<N>,步长等于深度限制 - 我们永远不会捕获关于fib<N>的模板深度限制。但我们不能在运行时写fib<500>::value。所以我们得到了解决方案 - 使用FibList<N>专门编写fib<N>::value的宏:

#define SetOptimizePointForFib(N) template<>\
struct FibList<N> {\
    FibList<(N)-1> previous;\
    size_t value;\
    FibList<N>() : value(fib<N>::value) {}\
};

我们必须写下这样的东西:

SetOptimizePointForFib(500);
SetOptimizePointForFib(1000);
SetOptimizePointForFib(1500);
SetOptimizePointForFib(2300);

所以我们确实编译了时间prealc并填充了很棒的静态数组。