C ++ 11: Calcul du temps de compilation d’un tableau

Supposons que j’ai une fonction constexpr f:

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

Et j’ai quelques const int N connus au moment de la compilation:

Non plus

 #define N ...; 

ou

 const int N = ...; 

selon vos besoins.

Je veux avoir un tableau int X:

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

de telle sorte que la fonction est évaluée au moment de la compilation et que les entrées de X sont calculées par le compilateur et que les résultats sont placés dans la zone statique de l’image de ma application exactement comme si

Y a-t-il un moyen d’écrire ceci? (Par exemple, avec des modèles ou des macros, etc.)

Meilleur j’ai: (Merci à Flexo)

 #include  #include  using namespace std; constexpr int N = 10; constexpr int f(int x) { return x*2; } typedef array A; template constexpr A fs() { return A{{ f(i)... }}; } template struct S; template struct S { static constexpr A gs() { return fs(); } }; template struct S { static constexpr A gs() { return S::gs(); } }; constexpr auto X = S::gs(); int main() { cout << X[3] << endl; } 

Il y a une solution pure C ++ 11 (pas de boost, pas de macros aussi) à ce problème. En utilisant le même truc que cette réponse, nous pouvons construire une séquence de nombres et les décompresser pour appeler f pour construire un std::array

 #include  #include  #include  #include  template struct seq { }; template struct gens : gens { }; template struct gens<0, S...> { typedef seq type; }; constexpr int f(int n) { return n; } template  class array_thinger { typedef typename gens::type list; template  static constexpr std::array make_arr(seq) { return std::array{{f(S)...}}; } public: static constexpr std::array arr = make_arr(list()); }; template  constexpr std::array array_thinger::arr; int main() { std::copy(begin(array_thinger<10>::arr), end(array_thinger<10>::arr), std::ostream_iterator(std::cout, "\n")); } 

(Testé avec g ++ 4.7)

Vous pouvez sauter std::array entièrement avec un peu plus de travail, mais je pense que dans ce cas, il est plus simple et simple d’utiliser simplement std::array .

Vous pouvez aussi le faire récursivement:

 #include  #include  #include  #include  #include  constexpr int f(int n) { return n; } template  constexpr typename std::enable_if>::type make() { return std::array{{Vals...}}; } template  constexpr typename std::enable_if>::type make() { return make(); } int main() { const auto arr = make<10>(); std::copy(begin(arr), end(arr), std::ostream_iterator(std::cout, "\n")); } 

Ce qui est sans doute plus simple.

Boost.Preprocessor peut vous aider. La ressortingction, cependant, est que vous devez utiliser un littéral intégral tel que 10 au lieu de N (même si c’est la constante de compilation):

 #include  #include  #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; } 

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, ~) }; 

élargit à ceci:

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

Une explication plus détaillée est ici:

  • BOOST_PP_ENUM

Si vous voulez que le tableau vive en mémoire statique, vous pouvez essayer ceci:

 template struct id { typedef T type; }; template struct int_pack {}; template struct make_int_range : make_int_range {}; template struct make_int_range<0,Tail...> : id> {}; #include  constexpr int f(int n) { return n*(n+1)/2; } template::type> struct my_lookup_table; template struct my_lookup_table> { static const int size = sizeof...(Indices); typedef std::array array_type; static const array_type& get() { static const array_type arr = {{f(Indices)...}}; return arr; } }; #include  int main() { auto& lut = my_lookup_table<>::get(); for (int i : lut) std::cout << i << std::endl; } 

Si vous souhaitez qu'une copie locale du tableau fonctionne, supprimez simplement l'esperluette.

J’ai légèrement étendu la réponse de Flexo et Andrew Tomazos pour que l’utilisateur puisse spécifier la plage de calcul et la fonction à évaluer.

 #include  #include  #include  template struct ComputeEngine { static const int lengthOfArray = max - min + sizeof... (expandedIndices) + 1; typedef std::array FactorArray; static constexpr FactorArray compute( ) { return ComputeEngine::compute( ); } }; template struct ComputeEngine { static const int lengthOfArray = sizeof... (expandedIndices) + 1; typedef std::array FactorArray; static constexpr FactorArray compute( ) { return FactorArray { { ComputePolicy::compute( min ), ComputePolicy::compute( expandedIndices )... } }; } }; /// compute 1/j struct ComputePolicy1 { typedef double ValueType; static constexpr ValueType compute( int i ) { return i > 0 ? 1.0 / i : 0.0; } }; /// compute j^2 struct ComputePolicy2 { typedef int ValueType; static constexpr ValueType compute( int i ) { return i * i; } }; constexpr auto factors1 = ComputeEngine::compute( ); constexpr auto factors2 = ComputeEngine::compute( ); int main( void ) { using namespace std; cout << "Values of factors1" << endl; for ( int i = 0; i < factors1.size( ); ++i ) { cout << setw( 4 ) << i << setw( 15 ) << factors1[i] << endl; } cout << "------------------------------------------" << endl; cout << "Values of factors2" << endl; for ( int i = 0; i < factors2.size( ); ++i ) { cout << setw( 4 ) << i << setw( 15 ) << factors2[i] << endl; } return 0; } 

Il y a pas mal de bonnes réponses ici. La question et les balises spécifient c++11 , mais comme quelques années se sont écastings, certains (comme moi) tombant sur cette question peuvent être ouverts à l’utilisation de c++14 . Si oui, il est possible de le faire très proprement et de manière concise en utilisant std::integer_sequence ; de plus, il peut être utilisé pour instancier des tableaux beaucoup plus longs, puisque le “Best I Have” actuel est limité par la profondeur de récursivité.

 constexpr std::size_t f(std::size_t x) { return x*x; } // A constexpr function constexpr std::size_t N = 5; // Length of array using TSequence = std::make_index_sequence; static_assert(std::is_same>::value, "Make index sequence uses std::size_t and produces a parameter pack from [0,N)"); using TArray = std::array; // When you call this function with a specific std::integer_sequence, // the parameter pack i... is used to deduce the the template parameter // pack. Once this is known, this parameter pack is expanded in // the body of the function, calling f(i) for each i in [0,N). template constexpr TArray get_array(std::integer_sequence) { return TArray{{ f(i)... }}; } int main() { constexpr auto s = TSequence(); constexpr auto a = get_array(s); for (const auto &i : a) std::cout << i << " "; // 0 1 4 9 16 return EXIT_SUCCESS; } 

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

 #include  constexpr int f(int i) { return 2 * i; } template  struct sequence { using result = sequence; static std::array apply() { return {{Ts...}}; } }; using v1 = sequence<1, 2, 3, 4>; using v2 = typename v1::result; int main() { auto x = v2::apply(); return 0; } 

Celui-ci, ça va?

 #include  #include  constexpr int f(int i) { return 2 * i; } template  struct t { using type = typename t::type; }; template  struct t<0u, Ts...> { using type = t<0u, Ts...>; static std::array apply() { return {{f(Ts)...}}; } }; int main() { using v = typename t<100>::type; auto x = v::apply(); }