c++ templates c++11 variadic-templates

c++ - Crear una matriz estática con plantillas variadic



variadic c++ (3)

Hubo una respuesta en stackoverflow (que parece que ya no puedo encontrar) que demostró cómo se puede usar una plantilla variadica en C ++ 11 para crear una matriz estática en tiempo de compilación:

template <class T, T... args> struct array_ { static const T data[sizeof...(args)]; }; template <class T, T... args> const T array_<T, args...>::data[sizeof...(args)] = { args... };

Se podría proporcionar una array_ recursiva para crear instancias de array_ con cualquier cantidad de parámetros, que luego se copiarán en tiempo de compilación en la matriz interna. Es una forma útil de crear meta-funciones para generar matrices constantes en tiempo de compilación.

Sin embargo, un problema es que depende de los parámetros de plantilla de clase para obtener los valores reales para poblar la matriz. Esto resulta en una limitación importante: solo se pueden usar constantes integrales como parámetros de plantilla de valor. Por lo tanto, no puede usar esta técnica para generar matrices de tipos personalizados.

Traté de pensar en algo para evitar esta limitación, pero no puedo encontrar nada. ¿Hay alguna manera de hacer que esta técnica funcione con constantes no integrales?


Bueno, puedes llenar una matriz estática con instancias de tipos personalizados (es decir, clases) siempre que sean constructables a partir de tipos enteros (o cualquier otro tipo que uno pueda proporcionar como parámetros no de plantilla y que no voy a enumerar aquí).

Simplemente observe el siguiente ejemplo, que creo que es lo suficientemente claro como para ser autoexplicativo:

#include <iostream> template<typename T> class my_class { public: my_class(T) { //construct } void print_something() { std::cout << "something/n"; } }; template<class C, class T, T ... args> struct array_ { static C data[sizeof...(args)]; }; template<class C, class T, T ... args> C array_<C, T, args...>::data[sizeof...(args)] = {C(args)...}; int main() { array_<my_class<int> , int, 1, 200, 0, 42>::data[2].print_something(); }

Nota: compilado muy bien bajo GCC 4.6


En C ++ 11 (y especialmente en C ++ 14), la mejor manera de inicializar objetos en tiempo de compilación es con constructores constexpr , no jugando metagames con el sistema tipo.

struct MyObject { int x_, y_; constexpr MyObject(int x, int y) : x_(x), y_(y) { } }; const MyObject array[] = { MyObject(1,2), MyObject(3,4) };

Aquí también puede aplicar su idea de "función del generador" si realmente desea:

#include <stdio.h> #if __cplusplus < 201400 template<size_t... II> struct integer_sequence { typedef integer_sequence type; }; template<size_t N, size_t... II> struct make_index_sequence; template<size_t... II> struct make_index_sequence<0, II...> : integer_sequence<II...> {}; template<size_t N, size_t... II> struct make_index_sequence : make_index_sequence<N-1, N-1, II...> {}; #define HACK(x) typename x::type #else #include <utility> // the C++14 way of doing things using std::integer_sequence; using std::make_index_sequence; #define HACK(x) x #endif struct MyObject { int x_, y_; constexpr MyObject(int x, int y) : x_(x), y_(y) { } }; template<typename T, int N, T (*Func)(int), typename Indices> struct GeneratedArrayHelper; template<typename T, int N, T (*Func)(int), size_t... i> struct GeneratedArrayHelper<T, N, Func, integer_sequence<i...>> { static const T data[N]; // element i is initialized with Func(i) }; template<typename T, int N, T (*Func)(int), size_t... i> const T GeneratedArrayHelper<T,N,Func, integer_sequence<i...>>::data[N] = { Func(i)... }; template<typename T, int N, T (*Func)(int)> struct GeneratedArray : GeneratedArrayHelper<T, N, Func, HACK(make_index_sequence<N>)> {}; constexpr MyObject newObject(int i) { return MyObject(2*i, 2*i+1); } int main() { for (const MyObject& m : GeneratedArray<MyObject, 5, newObject>::data) { printf("%d %d/n", m.x_, m.y_); } // Output: // 0 1 // 2 3 // 4 5 // 6 7 // 8 9 }

No sé por qué Clang 3.5 y GCC 4.8 insisten en que coloque la macro HACK() allí, pero se niegan a compilar el código sin ella. Probablemente cometí un error tonto y alguien puede señalarlo. Además, no estoy seguro de que todos los const y constexpr estén en los mejores lugares.


Los argumentos de plantilla sin tipo también pueden ser punteros o referencias, siempre que apunten o hagan referencia a un objeto con vinculación externa.

template<typename T, T& t> struct ref { static T& get() { return t; } }; int i = 0; int& ri = ref<int, i>::get(); // ok static int j = 0; int& rj = ref<int, j>::get(); // not ok const int jj = 0; // here, const implies internal linkage; whoops const int& rjj = ref<const int, jj>::get(); // not ok extern const int k = 0; const int& rk = ref<const int, k>::get(); // ok namespace { int l = 0; } int& rl = ref<int, l>::get(); // ok, and l is specific to the TU

Sin embargo, no creo que realmente quieras iniciar los elementos con referencias externas, ya que eso terminaría con el doble de objetos. Puede inicializar los elementos de la matriz a partir de literales, pero desafortunadamente no puede usar literales de cadena como argumentos de plantilla . Por lo tanto, necesitaría la proverbial capa de indirección: es doloroso porque las matrices o las referencias de matriz no pueden aparecer en una lista de parámetros de plantilla (supongo que esta es la razón por la cual los literales de cadena no pueden):

// Not possible: // const char* lits[] = { "Hello, ", "World!" }; // lit accepts const char*&, not const char* // typedef array_<T, lit<lits[0]>, lit<lits[1]>, int_<42> > array; // instead, but painful: const char* hello = "Hello"; const char* world = "World!"; typedef array_<T, lit<hello>, lit<world>, int_<42> > array; /* * here array::data would be an array of T, size 3, * initialized from { hello, world, 42 } */

No puedo ver cómo evitar la inicialización dinámica sin el constexpr C ++ 0x, e incluso entonces hay limitaciones. Usar algún tipo de tupla para construir inicializadores compuestos (por ejemplo, inicializar desde { { hello, world, 42 }, ... } ) que queda como ejercicio. Pero aquí hay un ejemplo .