40 votes

C ++11: Calcul du temps de compilation du tableau

Supposons que j'ai une certaine constexpr fonction f:

constexpr int f(int x) { ... }

Et j'ai quelques const int N connu au moment de la compilation:

Soit

#define N ...;

ou

const int N = ...;

selon les besoins de votre réponse.

Je veux avoir un tableau int X:

int X[N] = { f(0), f(1), f(2), ..., f(N-1) }

tels que la fonction est évaluée au moment de la compilation, et les entrées de X sont calculés par le compilateur et les résultats sont placés dans la zone statique de mon application image exactement comme si j'avais utilisé les littéraux entiers dans mon X liste d'initialiseur.

Est-il une manière que je peux écrire ceci? (Par exemple avec des modèles ou des macros et ainsi de suite)

Le meilleur que j'ai: (Merci à Flexo)

#include <iostream>
#include <array>
using namespace std;

constexpr int N = 10;
constexpr int f(int x) { return x*2; }

typedef array<int, N> A;

template<int... i> constexpr A fs() { return A{{ f(i)... }}; }

template<int...> struct S;

template<int... i> struct S<0,i...>
{ static constexpr A gs() { return fs<0,i...>(); } };

template<int i, int... j> struct S<i,j...>
{ static constexpr A gs() { return S<i-1,i,j...>::gs(); } };

constexpr auto X = S<N-1>::gs();

int main()
{
        cout << X[3] << endl;
}

30voto

Flexo Points 39273

Il est un pur C++11 (pas de boost, pas de macros trop) la solution à ce problème. En utilisant la même astuce que cette réponse nous pouvons construire une séquence de chiffres et de décompresser appelez - f à la construction d'un std::array:

#include <array>
#include <algorithm>
#include <iterator>
#include <iostream>

template<int ...>
struct seq { };

template<int N, int ...S>
struct gens : gens<N-1, N-1, S...> { };

template<int ...S>
struct gens<0, S...> {
  typedef seq<S...> type;
};

constexpr int f(int n) {
  return n;
}

template <int N>
class array_thinger {
  typedef typename gens<N>::type list;

  template <int ...S>
  static constexpr std::array<int,N> make_arr(seq<S...>) {
    return std::array<int,N>{{f(S)...}};
  }
public:
  static constexpr std::array<int,N> arr = make_arr(list()); 
};

template <int N>
constexpr std::array<int,N> array_thinger<N>::arr;

int main() {
  std::copy(begin(array_thinger<10>::arr), end(array_thinger<10>::arr), 
            std::ostream_iterator<int>(std::cout, "\n"));
}

(Testé avec g++ 4.7)

Vous pouvez ignorer std::array entièrement avec un peu plus de travail, mais je pense que dans ce cas c'est plus propre et plus simple pour utiliser std::array.

Vous pouvez également le faire de manière récursive:

#include <array>
#include <functional>
#include <algorithm>
#include <iterator>
#include <iostream>

constexpr int f(int n) {
  return n;
}

template <int N, int ...Vals>
constexpr
typename std::enable_if<N==sizeof...(Vals),std::array<int, N>>::type
make() {
  return std::array<int,N>{{Vals...}};
}

template <int N, int ...Vals>
constexpr
typename std::enable_if<N!=sizeof...(Vals), std::array<int,N>>::type 
make() {
  return make<N, Vals..., f(sizeof...(Vals))>();  
}

int main() {
  const auto arr = make<10>();
  std::copy(begin(arr), end(arr), std::ostream_iterator<int>(std::cout, "\n"));
}

Ce qui est sans doute plus simple.

4voto

Nawaz Points 148870

Coup de pouce.Préprocesseur peut vous aider. La restriction, cependant, est que vous devez utiliser l'intégrale littéral comme 10 au lieu de N (même soit constante à la compilation):

#include <iostream>

#include <boost/preprocessor/repetition/enum.hpp>

#define VALUE(z, n, text) f(n)

//ideone doesn't support Boost for C++11, so it is C++03 example, 
//so can't use constexpr in the function below
int f(int x) { return x * 10; }

int main() {
  int const a[] = { BOOST_PP_ENUM(10, VALUE, ~) };  //N = 10
  std::size_t const n = sizeof(a)/sizeof(int);
  std::cout << "count = " << n << "\n";
  for(std::size_t i = 0 ; i != n ; ++i ) 
    std::cout << a[i] << "\n";
  return 0;
}

De sortie (ideone):

count = 10
0
10
20
30
40
50
60
70
80
90

La macro dans la ligne suivante:

int const a[] = { BOOST_PP_ENUM(10, VALUE, ~) }; 

se développe pour cela:

int const a[] = {f(0), f(1), ... f(9)}; 

Un plus en détail l'explication est ici:

4voto

sellibitze Points 13607

Si vous souhaitez que le tableau vive dans la mémoire statique, vous pouvez essayer ceci:

 template<class T> struct id { typedef T type; };
template<int...> struct int_pack {};
template<int N, int...Tail> struct make_int_range
    : make_int_range<N-1,N-1,Tail...> {};
template<int...Tail> struct make_int_range<0,Tail...>
    : id<int_pack<Tail...>> {};

#include <array>

constexpr int f(int n) { return n*(n+1)/2; }

template<class Indices = typename make_int_range<10>::type>
struct my_lookup_table;
template<int...Indices>
struct my_lookup_table<int_pack<Indices...>>
{
    static const int size = sizeof...(Indices);
    typedef std::array<int,size> array_type;
    static const array_type& get()
    {
        static const array_type arr = {{f(Indices)...}};
        return arr;
    }
};

#include <iostream>

int main()
{
    auto& lut = my_lookup_table<>::get();
    for (int i : lut)
        std::cout << i << std::endl;
}
 

Si vous souhaitez qu'une copie locale de la baie fonctionne, retirez simplement l'esperluette.

1voto

void-pointer Points 2720

Voici une réponse plus concise où vous déclarez explicitement les éléments dans la séquence d'origine.

 #include <array>

constexpr int f(int i) { return 2 * i; }

template <int... Ts>
struct sequence
{
    using result = sequence<f(Ts)...>;
    static std::array<int, sizeof...(Ts)> apply() { return {{Ts...}}; }
};

using v1 = sequence<1, 2, 3, 4>;
using v2 = typename v1::result;

int main()
{
    auto x = v2::apply();
    return 0;
}
 

1voto

void-pointer Points 2720

Celui-ci, ça va?

 #include <array>
#include <iostream>

constexpr int f(int i) { return 2 * i; }

template <int N, int... Ts>
struct t { using type = typename t<N - 1, Ts..., 101 - N>::type; };

template <int... Ts>
struct t<0u, Ts...>
{
    using type = t<0u, Ts...>;
    static std::array<int, sizeof...(Ts)> apply() { return {{f(Ts)...}}; }
};

int main()
{
    using v = typename t<100>::type;
    auto x = v::apply();
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X