and c++ templates c++11 constexpr

and - constexpr in c++



C++ 11: cálculo del tiempo de compilación de la matriz (7)

Supongamos que tengo alguna función constexpr f:

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

Y tengo alguna const int N conocida en tiempo de compilació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 compilación, y el compilador 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 <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; }


¿Que tal este?

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


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 <array> #include <iostream> #include <iomanip> template<typename ComputePolicy, int min, int max, int ... expandedIndices> struct ComputeEngine { static const int lengthOfArray = max - min + sizeof... (expandedIndices) + 1; typedef std::array<typename ComputePolicy::ValueType, lengthOfArray> FactorArray; static constexpr FactorArray compute( ) { return ComputeEngine<ComputePolicy, min, max - 1, max, expandedIndices...>::compute( ); } }; template<typename ComputePolicy, int min, int ... expandedIndices> struct ComputeEngine<ComputePolicy, min, min, expandedIndices...> { static const int lengthOfArray = sizeof... (expandedIndices) + 1; typedef std::array<typename ComputePolicy::ValueType, lengthOfArray> 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<ComputePolicy1, 4, 7>::compute( ); constexpr auto factors2 = ComputeEngine<ComputePolicy2, 3, 9>::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 etiquetas 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<N>; static_assert(std::is_same<TSequence, std::integer_sequence<std::size_t, 0, 1, 2, 3, 4>>::value, "Make index sequence uses std::size_t and produces a parameter pack from [0,N)"); using TArray = std::array<std::size_t,N>; // 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<std::size_t...i> constexpr TArray get_array(std::integer_sequence<std::size_t,i...>) { 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 <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; }


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 compilación):

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

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í:


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

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

Lo cual es discutiblemente más simple.


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

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