是否有可能在编译时评估数组?

时间:2016-02-14 07:42:25

标签: c++ algorithm c++11 constexpr

我需要存储前N个Fibonacci数的数组。

const int N = 100;
long long int fib[N] = {0};
fib[0] = 1;
fib[1] = 1;
for(int i = 2; i < N; ++i)
    fib[i] = fib[i-2] + fib[i-1];
return 0;

是否可以制作 fib [] constexpr,并在编译时以某种方式评估它?

5 个答案:

答案 0 :(得分:2)

有一种方法(丑陋的),但我无法想到其他任何事情。

#include <iostream>
#include <cmath>

constexpr unsigned long long f(int x)
{
    return 1/sqrt(5)*pow(((1+sqrt(5))/2),x) - 1/sqrt(5)*pow(((1-sqrt(5))/2),x);
}

#define FIBB1(x)  1
#define FIBB2(x)  FIBB1(x-1),1
#define FIBB3(x)  FIBB2(x-1),f(x)
#define FIBB4(x)  FIBB3(x-1),f(x)
#define FIBB5(x)  FIBB4(x-1),f(x)
#define FIBB6(x)  FIBB5(x-1),f(x)
#define FIBB7(x)  FIBB6(x-1),f(x)
#define FIBB8(x)  FIBB7(x-1),f(x)
#define FIBB9(x)  FIBB8(x-1),f(x)
#define FIBB10(x) FIBB9(x-1),f(x)
#define FIBB11(x) FIBB10(x-1),f(x)
#define FIBB12(x) FIBB11(x-1),f(x)
#define FIBB13(x) FIBB12(x-1),f(x)
#define FIBB14(x) FIBB13(x-1),f(x)
#define FIBB15(x) FIBB14(x-1),f(x)
#define FIBB16(x) FIBB15(x-1),f(x)
#define FIBB17(x) FIBB16(x-1),f(x)
#define FIBB18(x) FIBB17(x-1),f(x)
#define FIBB19(x) FIBB18(x-1),f(x)
#define FIBB20(x) FIBB19(x-1),f(x)
// ...
#define FIBB93(x) FIBB92(x-1),f(x)
//#define FIBB94(x) FIBB93(x-1),f(x) //unsigned long long overflow, can't calculate more

#define FIBB(x) {FIBB##x(x)}

constexpr unsigned long long fib[93] = FIBB(93);

int main()
{
    // all possible fibbonacci numbers for unsigned long long implementation
    for(int i=0; i<93; ++i)
        std::cout << fib[i] << std::endl;
}

我认为这是C ++内置数组的唯一途径。

答案 1 :(得分:2)

首先,您必须在编译时版本中编写Fibonacci算法,因此请考虑以下内容:

template <size_t N>
struct Fibo {
    static constexpr const size_t value {Fibo<N-2>::value + Fibo<N-1>::value};
};

template <>
struct Fibo<0> {
    static constexpr const size_t value {1};
};

template <>
struct Fibo<1> {
    static constexpr const size_t value {1};
};

你可以像这样简单地使用它:

std::cout << Fibo<0>::value << std::endl;
std::cout << Fibo<1>::value << std::endl;
std::cout << Fibo<2>::value << std::endl;
std::cout << Fibo<3>::value << std::endl;
std::cout << Fibo<10>::value << std::endl;
std::cout << Fibo<50>::value << std::endl;

,输出值为:

1
1
2
3
89
20365011074

但这仍然不是你想要的。

我不知道你是否可以制作constexpr数组(但可能有可能),但你可以稍微改变一下。考虑:

template <size_t N>
struct Storage {
    static size_t data[N+1];
};

template <size_t N> size_t Storage<N>::data[N+1] {};

template <size_t N, size_t F>
struct Filler {
    static constexpr void fill () {
        Storage<N>::data[F] = Fibo<F>::value;
        Filler<N, F-1>::fill ();
    }
};

template <size_t N>
struct Filler<N, 0> {
    static constexpr void fill () { 
        Storage<N>::data[0] = Fibo<0>::value;
    }
};

template <size_t N>
struct Calc {
    static constexpr void calc () {        
        Filler<N, N>::fill ();
    }
};

,用法如下:

constexpr const size_t N = 12;

Calc<N>::calc ();
size_t* ptr = Storage<N>::data;

for (int i = 0; i <= N; ++i) {
    std::cout << ptr[i] << std::endl;
}

并输出:

1
1
2
3
5
8
13
21
34
55
89
144
233

这里重要的是Storage类,它使用适当数量的元素存储我们的数组。

常规Filler类(带有两个模板参数)用于可传递的任何F值,值0除外。因为如果我们达到0指数,我们就不会想再次召唤fill()成员函数,因为我们已经完成了。这就是Filler类的部分特化存在的原因。

希望我能帮忙解决这个问题。

答案 2 :(得分:2)

这是一个C ++ 14解决方案(GCC&gt; = 5.0.0,Clang&gt; = 3.5.0),使用模板参数作为长度。您在constexpr函数中编写了一个命令式循环(与原始帖子相同)。使用disassembler,您可以看到序列作为原始数据嵌入到程序中,即使没有优化(-O0)。

#include <array>
#include <cstddef>
#include <iostream>
#include <type_traits>
#include <utility>

namespace {
// Create an std::array from a C array (internal) via an
// std::index_sequence.
template <typename T, typename TSequence> struct MakeArrayImpl;
template <typename T, std::size_t... TIndices>
struct MakeArrayImpl<T, std::index_sequence<TIndices...>> {
  static constexpr std::array<T, sizeof...(TIndices)>
  make_array(T values[sizeof...(TIndices)]) {
    return std::array<T, sizeof...(TIndices)>{{values[TIndices]...}};
  }
};

// Create an std::array from a C array.
template <typename T, std::size_t TLength>
constexpr std::array<T, TLength> make_array(T values[TLength]) {
  return MakeArrayImpl<T, std::make_index_sequence<TLength>>::make_array(
      values);
}

// Return an std::array of the first numbers in the Fibonacci sequence.
template <std::size_t TLength>
constexpr std::array<long long int, TLength> fibs() {
  // Original algorithm.
  long long int fib[TLength] = {0};
  fib[0] = 1;
  fib[1] = 1;
  for (std::size_t i = 2; i < TLength; ++i) {
    fib[i] = fib[i - 2] + fib[i - 1];
  }
  return make_array<long long int, TLength>(fib);
}
}

int main() {
  // Original algorithm.
  const int N = 92;
  long long int fib[N] = {0};
  fib[0] = 1;
  fib[1] = 1;
  for (int i = 2; i < N; ++i)
    fib[i] = fib[i - 2] + fib[i - 1];

  // Test constexpr algorithm against original algorithm.
  static constexpr auto values = fibs<N>();
  static_assert(values.size() == N, "Expected N values in Fibs");
  for (int i = 0; i < N; ++i) {
    if (fib[i] != values[i]) {
      std::cerr << "Mismatch at index " << i << "\n";
      std::cerr << "Expected: " << fib[i] << "\n";
      std::cerr << "Actual  : " << values[i] << "\n";
    }
  }
}

答案 3 :(得分:1)

  1. 在您发布的代码示例中,如果使用-O3优化,编译器可能会自行展开循环或至少部分循环。在godbolt上玩,看起来这不会发生在N=100,而是发生在N最多约40个。在这种情况下,它确实发生在编译时,无论是否是constexpr

  2. 这也指出 - 在许多机器上,long long int不足以容纳第100个斐波纳契数。斐波纳契数以指数方式增长,你应该期望第100个数要求大约100位左右。在典型的计算机上,由于整数溢出,您编写的代码将显示未定义的行为。

  3. 使用模板可以这样做:

    // Fibonacci recurrence
    template <long int n> 
    struct fib_pair {
      typedef fib_pair<n-1> prev;
      static constexpr long int fib_n = prev::fib_n_plus_one;
      static constexpr long int fib_n_plus_one = prev::fib_n + prev::fib_n_plus_one;
    };
    
    template <>
    struct fib_pair<0> {
      static constexpr long int fib_n = 0;
      static constexpr long int fib_n_plus_one = 1;
    };
    
    // List structure
    template <long int ... > struct list {};
    
    // Concat metafunction
    template <typename A, typename B> struct concat;
    template <long int... As, long int... Bs> struct concat<list<As...>, list<Bs...>> {
      typedef list<As..., Bs...> type;
    };
    
    // Get a sequence from the fib_pairs
    template <long int n>
    struct fib_seq {
      typedef typename fib_seq<n-1>::type prev;
    
      typedef typename concat<prev, list<fib_pair<n>::fib_n>>::type type;
    };
    
    template <>
    struct fib_seq<0> {
      typedef list<0> type;
    };
    
    
    // Make an array from pack expansion
    #include <array>
    template <typename T> struct helper;
    
    template <long int ... nums>
    struct helper <list<nums...>> {
      typedef std::array<const long int, sizeof...(nums)> array_type;
      static constexpr array_type get_array() {
        return {{ nums... }};
      }
    };
    
    // Easy access
    template <long int n>
    constexpr std::array<const long int, n + 1> get_fib_array() {
      return helper<typename fib_seq<n>::type>::get_array();
    }
    
    #include <iostream>
    
    int main () {
      for (const long int x : get_fib_array<15>()) {
        std::cout << x << std::endl;
      }
    }
    

答案 4 :(得分:1)

这是使用C ++ 14库特征[1](GCC&gt; = 4.9.0,Clang&gt; = 3.5.0)的C ++ 11解决方案,使用模板参数作为长度。你使用递归编写一个循环。使用disassembler,您可以看到序列作为原始数据嵌入到程序中,即使没有优化(-O0)。

[1] std::index_sequence可以在C ++ 11中自行实现,如果标准库中没有它。

#include <array>
#include <cstddef>
#include <iostream>
#include <type_traits>
#include <utility>

namespace {
// Create an std::array from a C array (internal) via an
// std::index_sequence.
template <typename T, typename TSequence> struct MakeArrayImpl;
template <typename T, std::size_t... TIndices>
struct MakeArrayImpl<T, std::index_sequence<TIndices...>> {
  static constexpr std::array<T, sizeof...(TIndices)>
  make_array(T values[sizeof...(TIndices)]) {
    return std::array<T, sizeof...(TIndices)>{{values[TIndices]...}};
  }
};

// Create an std::array from a C array.
template <typename T, std::size_t TLength>
constexpr std::array<T, TLength> make_array(T values[TLength]) {
  return MakeArrayImpl<T, std::make_index_sequence<TLength>>::make_array(
      values);
}

// Return an std::array of the first numbers in the Fibonacci sequence.
template <std::size_t TLength>
constexpr std::array<long long int, TLength> fibs() {
  // Original algorithm.
  long long int fib[TLength] = {0};
  fib[0] = 1;
  fib[1] = 1;
  for (std::size_t i = 2; i < TLength; ++i) {
    fib[i] = fib[i - 2] + fib[i - 1];
  }
  return make_array<long long int, TLength>(fib);
}
}

int main() {
  // Original algorithm.
  const int N = 92;
  long long int fib[N] = {0};
  fib[0] = 1;
  fib[1] = 1;
  for (int i = 2; i < N; ++i)
    fib[i] = fib[i - 2] + fib[i - 1];

  // Test constexpr algorithm against original algorithm.
  static constexpr auto values = fibs<N>();
  static_assert(values.size() == N, "Expected N values in Fibs");
  for (int i = 0; i < N; ++i) {
    if (fib[i] != values[i]) {
      std::cerr << "Mismatch at index " << i << "\n";
      std::cerr << "Expected: " << fib[i] << "\n";
      std::cerr << "Actual  : " << values[i] << "\n";
    }
  }
}