C ++ 11: cálculo del tiempo de comstackción de la matriz

Supongamos que tengo alguna función constexpr f:

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

Y tengo alguna const int N conocida en tiempo de comstackción:

Ya sea

 #define N ...; 

o

 const int N = ...; 

según sea necesario por tu respuesta.

Quiero tener una matriz int X:

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

tal que la función se evalúa en tiempo de comstackción, y el comstackdor calcula las entradas en X y los resultados se colocan en el área estática de la imagen de la aplicación exactamente como si hubiera usado literales enteros en mi lista de inicializadores X.

¿Hay alguna forma de que pueda escribir esto? (Por ejemplo, con plantillas o macros, etc.)

Lo mejor que tengo: (Gracias a 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; } 

Hay una solución pura de C ++ 11 (sin refuerzo, sin macros) para este problema. Usando el mismo truco como esta respuesta , podemos construir una secuencia de números y descomprimirlos para llamar a f para construir 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")); } 

(Probado con g ++ 4.7)

Puede omitir std::array completo con un poco más de trabajo, pero creo que en este caso es más simple y más simple usar std::array .

También puedes hacer esto recursivamente:

 #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")); } 

Lo cual es discutiblemente más simple.

Boost.Preprocessor puede ayudarlo. La restricción, sin embargo, es que debe usar literal integral como 10 lugar de N (incluso si se trata de una constante de tiempo de comstackción):

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

Salida ( ideone ):

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

La macro en la siguiente línea:

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

se expande a esto:

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

Una explicación más detallada está aquí:

  • BOOST_PP_ENUM

Si desea que la matriz viva en memoria estática, puede intentar esto:

 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 desea una copia local de la matriz para trabajar, simplemente elimine el ampersand.

Amplié ligeramente la respuesta de Flexo y Andrew Tomazos para que el usuario pueda especificar el rango computacional y la función a evaluar.

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

Aquí hay algunas buenas respuestas. La pregunta y las tags especifican c++11 , pero como han pasado algunos años, algunos (como yo) tropezando con esta pregunta pueden estar abiertos a usar c++14 . Si es así, es posible hacer esto de manera muy limpia y concisa usando std::integer_sequence ; además, se puede usar para crear instancias mucho más largas, ya que el “Mejor que tengo” actual está limitado por la profundidad de recursión.

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

Aquí hay una respuesta más concisa donde declaras explícitamente los elementos en la secuencia original.

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

¿Que tal este?

 #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(); }