2011-09-18 26 views
7

ho questo modello di classe:modelli variadic e nuova

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

Questo funziona bene per un argomento di template, ma per più argomenti ottengo questo errore:

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

Come posso utilizzare i modelli variadic con new in questo modo? Qual è la sintassi corretta?


EDIT: Penso che la mia domanda non fosse abbastanza chiara. Quello che voglio è questo:

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

mio compilatore è 163.7.1 clang con questo flag: -std=c++0x.

+0

'std :: vector test_cases;' sembra strano poiché T è più di un tipo lì. – Flexo

+0

@awoodland hai ragione. Grazie per aver individuato questo. In effetti, ogni 'T' è una sottoclasse di' TestCase' (ecco perché uso i puntatori). Lo cambierò. –

+0

Si potrebbe provare 'test_cases.push_back (new T()) ...;'. –

risposta

3

vector::push_back si aspetta un parametro in modo non è possibile espandere il modello variadic nella chiamata di funzione. Inoltre ho aggiunto un parametro template per la classe base (da cui derivano tutte le altre classi).

Ecco qualcosa che 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

Forse vuoi una tupla all'interno del tuo std :: vector? Non sono sicuro se questo è ciò che si intende, ma questo compila almeno sul mio 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

Perché 'to_pointer :: type' e non solo' T * '? – Dani

+0

Sì, buon punto ... Risolto. Stavo scherzando con i modi per farlo compilare, e non pensavo all'ovvio :) – Maister

0

Mi colpisce che si desidera un vettore dinamico della qualsiasi tipo (anche se non personalmente mi guardare, mi è stato detto da un amico che apparentemente c'era qualcosa di simile nella libreria boost), a differenza di un vettore modello.

Un modello di vettore è fondamentalmente un vettore che può assumere qualsiasi uno tipo definito (o tutti interi, o tutte doppie, o tutti i carri ma non ints e doppie e float).

La ragione per cui non esiste una classe come questa è perché ogni elemento occupa una diversa dimensione di blocco in memoria (un char è un byte, un int può essere 4 byte ecc. Ecc.) E richiederebbe risorse aggiuntive alla ricerca di sapere cosa aspettarsi (la memoria normale è contigua ... quale è un vettore, dato che è 'fondamentalmente' una matrice).

Se stai cercando di creare il tuo (ho provato), stai osservando i puntatori void *, l'allocazione dinamica della memoria e tutta una serie di mal di testa che coinvolgono il typecasting (non sono a conoscenza di alcun metodo automatico per tipizzare correttamente un elemento dietro le quinte, ma altri potrebbero essere in grado di chip in).

+1

Ciò che OP vuole è del tutto possibile. Si noti che sta usando esplicitamente un vettore di * puntatori *. Questo consente al polimorfismo di avere luogo se tutti i tipi hanno una classe base comune ('TestCase', nel suo esempio). –

2

L'espansione del pacchetto può avvenire solo in un numero selezionato di situazioni e non funziona per espressioni o istruzioni arbitrarie. Tuttavia, poiché una di queste situazioni è l'inizializzazione dell'elenco e poiché l'ordine delle operazioni è definito per gli inizializzatori di controvento della sintassi di inizializzazione dell'elenco, è sempre possibile espandere un'istruzione arbitraria. Vale a dire:

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

Il void() trucco è quello di sopprimere ogni invocazione di un sovraccarico operator,. Completamente irrilevante in questa sede, ma ho incluso che dal momento che può essere utile quando refactoring la funzionalità in 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

Divertente. Ho scritto una risposta che era quasi identica a questa, senza accorgertene fino ad ora che l'avevi già (ho cambiato la mia risposta per non ripetere più quello che hai detto ora :)). Ma hai dimenticato Parens intorno a '(exp), void(), 0'. Come è adesso, sarebbe un errore cercare di espandere un '0' letterale. –

+0

@Johannes corretto! –

2

È possibile raggiungere questo obiettivo, ma è un po 'rotonda dal momento che si scrive direttamente l'espressione. È necessario chiamare push_back una volta per ogni argomento nell'elenco di argomenti del modello variadic.

Come si ottiene questo?Beh, chiamando una funzione ricorsiva una volta per ogni argomento di un template:

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

Qui abbiamo due overload della funzione fill, una con un elenco di modelli argomenti variadic e uno senza - questo è il caso base ricorsione. Finché ci sono ancora almeno due argomenti del template, viene chiamata la prima versione. Se rimane solo un argomento, viene chiamato il secondo argomento.

chiamare in questo modo nel costruttore:

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

Tra i lati positivi, ci si può aspettare che questo sia ottimizzato dal compilatore, no? – bitmask

+0

@bitmask Non sono sicuro di cosa intendi per "ottimizzato". Poiché i template sono completamente risolti al momento della compilazione, questo * sarà * completamente risolto al momento della compilazione. Se si fa riferimento a inlining allora sì, le chiamate "ricorsive" 'fill' saranno completamente in linea. –

+0

Sì, volevo dire inlining. – bitmask

1

In una nota correlata, in questo caso particolare è possibile utilizzare initializer_list sostegno del vettore scrivendo il costruttore come segue

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

O utilizzando l'assegnazione se per qualsiasi motivo non è possibile utilizzare inizializzatori del costruttore

Test() { 
    test_cases = { new T ... }; 
} 
Problemi correlati