2015-03-05 6 views
7

Voglio scrivere un metodo di classe che prende un parametro pacchetto modello, ma zero argomenti, e "iterate" sui tipi:ricorsive Funzione Variadica template

struct Bar { 
    template <typename T, typename... Ts> 
    void foo() { 
     // something with T that involves Bar's members 
     foo<Ts...>(); 
    } 
}; 

Qual è il modo migliore per implementare questo?

risposta

3

'possibile utilizzare il seguente:

struct Bar { 
    template <typename... Ts> 
    void foo() { 
     int dummy[] = {0 /*Manage case where Ts is empty*/, 
         (bar<Ts>(), void() /* To avoid overload `operator,` */, 0)...}; 
     (void) dummy; // suppress warning for unused variable. 
    } 

    template <typename T> 
    void bar() 
    { 
     // something with T that involves Bar's members 
    } 

}; 

In C++ 17, può essere semplificata con l'espressione piegatura:

struct Bar { 
    template <typename... Ts> 
    void foo() { 
     (static_cast<void>(bar<Ts>()), ...); 
    } 

    template <typename T> 
    void bar() 
    { 
     // something with T that involves Bar's members 
    } 

}; 
0

mi piace funzioni sovraccaricato e utilizzando un typelist:

#include <iostream> 
#include <typeinfo> 

template <typename ...Ts> struct typelist { }; 

void foo_impl(typelist<>) 
{ 
    // we are finished 
} 

template <typename T, typename ...Ts> 
void foo_impl(typelist<T, Ts...>) 
{ 
    std::cout << typeid(T).name() << ", "; 
    foo_impl(typelist<Ts...>{}); 
} 



template <typename ...Ts> 
void foo() 
{ 
    std::cout << "called with <"; 
    foo_impl(typelist<Ts...>{}); 
    std::cout << ">" << std::endl; 
} 


int main() 
{ 
    foo<int, char, float>(); 
} 
+0

eh? 'pippo' non accetta argomenti. – MadScientist

+0

Come svantaggio, richiede N passaggi ricorsivi per N tipi, e istanze tipi e funzioni il cui nome lunghezza somma a O (N^2). Fare quanto sopra con N modestamente grande causa una compilazione lenta, e può portare a un numero di caratteri binari se quei nomi non vengono rimossi dal compilatore. – Yakk

3
template<class...Fs> 
void do_in_order(Fs&&...fs) { 
    int _[]={0, (std::forward<Fs>(fs)(), void(), 0)...}; 
    (void)_; 
} 

nasconde il sintassi richiesta per eseguire un pacchetto di oggetti funzione nell'ordine da sinistra a destra.

Poi:

struct Bar { 
    template <class... Ts> 
    void foo() { 
    do_in_order([&]{ 
     using T = Ts; 
     // code 
    }...); 
    } 
}; 

e in un compilatore conforme, verrà eseguito il // code con T essere ogni tipo da sinistra a destra.

Si noti che alcuni compilatori che affermano di essere compilatori C++ 11 potrebbero non riuscire a compilare quanto sopra.

Il vantaggio di questa tecnica è che nasconde il brutto codice "espandi e valuta modelli" all'interno di una funzione con un nome chiaro. Scrivi do_in_order una volta, e di solito è sufficiente per quasi ogni uso di quel trucco di espansione dell'array.

Ci sono due motivi importanti per utilizzare questo tipo di sintassi esoterica invece delle soluzioni ricorsive "più semplici".

In primo luogo, rende le cose più facili per l'ottimizzatore. Gli ottimizzatori a volte si arrendono dopo una pila di chiamate ricorsive.

In secondo luogo, la somma dei nomi delle lunghezze delle firme di funzione per le funzioni ricorsive tradizionali aumenta a O (n^2). Se si usano i tipi di aiuto, la lunghezza totale dei nomi è anche O (n^2). A meno che tu non stia attento, questo può causare il tempo di compilazione, il tempo di collegamento e le dimensioni binarie gonfie.

In C++ 1z ci sono piani per una sintassi di "piega" che potrebbe rendere meno esoteriche le parti esoteriche di cui sopra.

+0

Questo è abbastanza pulito. Apparentemente, gcc 4.9.2 è uno di quei "compilatori che affermano di essere compilatori C++ 11" che non riesce a compilarlo. – Barry

+0

@Barry ayep. Clang penso che funzioni. Per qualche ragione, gcc pensa che un pacchetto di parametri non espansi alla fine di una dichiarazione sia un errore. Sciocco gcc: le dichiarazioni possono essere solo una parte di un modello di espansione. – Yakk