2011-09-18 14 views
7

tengo esta plantilla de clase:plantillas variadic y nueva

template<class... T> 
class Test { 
    std::vector<TestCase*> test_cases; 
public: 
    Test() { 
    // Here, for each T an instance should be added to test_cases. 
    test_cases.push_back((new T)...); 
    } 
}; 

Esto funciona bien para un parámetro de plantilla, pero para múltiples argumentos me sale este error:

error: too many arguments to function call, expected 1, have 2 

¿Cómo puedo utilizar las plantillas variadic con new de esta manera? ¿Cual es la sintaxis correcta?


EDIT: Creo que mi pregunta no era del todo clara. Lo que yo quiero es éste:

Test<TestCase1, TestCase2, TestCase3>; 
// The constructor will then be: 
test_cases.push_back(new TestCase1); 
test_cases.push_back(new TestCase2); 
test_cases.push_back(new TestCase3); 

Mi compilador es 163.7.1 sonido metálico con esta bandera: -std=c++0x.

+0

'std :: vector test_cases;' parece extraño porque T es más de un tipo allí. – Flexo

+0

@awoodland tienes razón. Gracias por descubrir esto De hecho, cada 'T' es una subclase de' TestCase' (por eso uso punteros). Voy a cambiar esto. –

+0

Puede probar 'test_cases.push_back (new T()) ...;'. –

Respuesta

3

vector::push_back espera un parámetro de lo que no puede ampliar la plantilla variadic en la llamada de función. También agregué un parámetro de plantilla para la clase base (de la cual derivan todas las demás clases).

Esto es algo que compiles.

struct base{}; 
struct d0 : base{}; 
struct d1 : base{}; 
struct d2 : base{}; 

#include <vector> 

// termination condition for helper function 
template <class T> 
void add(std::vector<T*>&) { 
} 

// helper function 
template <class T, class Head, class... Tail> 
void add(std::vector<T*>& v) { 
     v.push_back(new Head()); 
     add<T, Tail...>(v); 
} 

template <class T, class ... U> 
class test 
{ 
    std::vector<T*> vec; 
public: 
    test() { 
     add<T, U...>(vec);  
    } 
}; 

int main() 
{ 
    test<base, d0,d1,d2> t; 
} 
0

Tal vez quieras una tupla dentro de tu std :: vector? No estoy seguro si esto es lo que pretende, pero esto compila al menos en mi G ++ 4.6.1: D

#include <vector> 
#include <utility> 
#include <functional> 
#include <string> 

template<class... T> 
class Test { 
    std::vector<std::tuple<T*...>> test_cases; 
public: 
    Test() { 
    // Here, for each T an instance should be added to test_cases. 
    test_cases.push_back(std::tuple<T*...>((new T)...)); 
    } 
}; 

int main() 
{ 
    Test<int, float> foo; 
    Test<std::string, double> bar; 
} 
+1

¿Por qué 'to_pointer :: type' y no solo' T * '? – Dani

+0

Sí, buen punto ... Se arregló. Estaba jugando con formas de compilar, y no pensé en lo obvio :) – Maister

0

Me llama la atención que desea un vector dinámico de la cualquier tipo (aunque no personalmente mirando a mí mismo, un amigo me dijo que aparentemente había algo como esto en la biblioteca de impulso), a diferencia de un vector de plantilla.

Un vector plantilla es básicamente un vector que puede asumir cualquiera de uno tipo definido (o bien todos enteros, o todas dobles, o todos los flotadores, pero no Ints y dobles y flotadores).

La razón por la cual no existe una clase como esta convencionalmente es porque cada elemento ocupa un tamaño de bloque diferente en la memoria (un carácter es un byte, un int podría ser de 4 bytes, etc.) y tomaría recursos adicionales en la búsqueda para saber qué esperar (el almacenamiento normal es contiguo ... que es un vector, dado que es 'básicamente' una matriz).

Si está buscando construir uno propio (lo intenté), está buscando nulo * punteros, asignación de memoria dinámica y toda una serie de dolores de cabeza que involucran encasillado (no conozco ningún método automatizado para encasillar correctamente un elemento detrás de la escena, pero otros podrían ser capaces de ingresar).

+1

Lo que OP quiere es completamente posible. Tenga en cuenta que está usando explícitamente un vector de * punteros *. Esto permite que el polimorfismo tenga lugar si todos los tipos tienen una clase base común ('TestCase', en su ejemplo). –

2

La expansión de paquetes solo puede ocurrir en un número selecto de situaciones y no funciona para expresiones o declaraciones arbitrarias. Sin embargo, dado que una de esas situaciones es la inicialización de listas y dado que el orden de las operaciones se define para los inicializadores de corchetes de la sintaxis de inicialización de listas, siempre es posible expandir una declaración arbitraria. A saber:

typedef std::initializer_list<int> expand; 
expand { (test_cases.push_back(new T), void(), 0)... }; 

El truco es void() para suprimir cualquier invocación de una operator, sobrecargado. Completamente irrelevante aquí, pero lo he incluido ya que puede ser útil al refactorizar la funcionalidad en una macro:

#define EXPAND(exp) \ 
    std::initializer_list<int> { ((exp), void(), 0)... } 

// No use of '...', it's in the macro body 
EXPAND((test_cases.push_back(new T))); 
+0

Gracioso. Escribí una respuesta que era casi idéntica a esta sin notar hasta ahora que ya la tenías (cambié mi respuesta para no repetir lo que dijiste más ahora :)). Pero olvidaste parens alrededor de '(exp), void(), 0'. Como es ahora, sería un error intentar expandir un '0' literal. –

+0

@Johannes ¡Reparado! –

2

Usted puede lograr esto, pero es un poco desde la rotonda se escribe la expresión directa. Debe llamar al push_back una vez para cada argumento en la lista de argumentos de plantilla variadic.

¿Cómo lo logras?Bueno, llamando a una función recursiva una vez para cada parámetro de plantilla:

template <typename Base, typename T1, typename T2, typename... T> 
void fill(std::vector<Base*>& vec) { 
    vec.push_back(new T1); 
    fill<Base, T2, T...>(vec); 
} 

template <typename Base, typename T1> 
void fill(std::vector<Base*>& vec) { 
    vec.push_back(new T1); 
} 

Aquí tenemos dos sobrecargas de la función fill, uno con una lista de argumentos de plantilla variadic y otro sin él - este es el caso base recursividad. Siempre que haya al menos dos argumentos de plantilla, se llama a la primera versión. Si solo queda un único argumento, se llama al segundo argumento.

Llamada así en el constructor:

fill<TestCase, T...>(test_cases); 
+0

En el lado positivo, puede esperar que el compilador optimice esto, ¿o no? – bitmask

+0

@bitmask No estoy seguro de lo que quiere decir con "optimizado". Dado que las plantillas se resuelven por completo en el momento de la compilación, este * * se resolverá completamente en el momento de la compilación. Si se refiere a la alineación, entonces sí, las llamadas de "relleno" "recursivas" estarán completamente en línea. –

+0

Sí, quise decir en línea. – bitmask

1

En una nota relacionada, en este caso particular se puede utilizar initializer_list apoyo del vector escribiendo el constructor de la siguiente manera

Test() 
:test_cases{ new T ... } 
{ } 

o mediante la asignación si por alguna razón no se puede utilizar inicializadores del constructor

Test() { 
    test_cases = { new T ... }; 
} 
Cuestiones relacionadas