2013-03-08 33 views
5

In pre-11 C++ ho avuto qualcosa di simile:variadic modello di classe di base trasferimento di chiamata

template<class T,class U,class V> 
struct Foo : T,U,V { 

    bool init() { 

    if(!T::init() || !U::init() || !V::init()) 
     return false; 

    // do local init and return true/false 
    } 
}; 

mi piacerebbe convertire questo in C++ 11 sintassi variadic per ottenere il beneficio dell'argomento flessibile di lunghezza elenco. Capisco il concetto di decomprimere la lista degli argomenti usando la ricorsione ma non riesco a vedere la sintassi giusta. Ecco quello che ho provato:

template<typename... Features> 
struct Foo : Features... { 

    template<typename F,typename... G> 
    bool recinit(F& arg,G&& ...args) { 

    if(!F::init()) 
     return false; 

    return recinit<F,G...>(args...); 
    } 

    bool init() { 
    // how to call recinit() from here? 
    } 
}; 

io preferirei l'ordine delle chiamate al init classe di base() funzioni da sinistra a destra, ma non è critica.

+2

Non sai cosa stai cercando di ottenere: la tua funzione 'recinit' è infinitamente ricorsiva, o mi sbaglio? come pensi di chiamarlo da 'init()', con quali argomenti? –

+3

Interrompere l'utilizzo di un metodo 'init' e utilizzare un costruttore. –

risposta

4

questo dovrebbe funzionare:

template<typename F, typename... T> 
    struct recinit; 

template<typename F> 
    struct recinit<F> { 
     static bool tinit(F *) { 
      return true; 
     } 
    }; 
template<typename F, typename T, typename... G> 
    struct recinit<F, T, G...> { 
     static bool tinit(F *ptr) { 
      if (!ptr->T::init()) 
       return false; 
      return recinit<F, G...>::tinit(ptr); 
     } 
    }; 

template<typename... Features> 
struct Foo : Features... { 

    bool init() { 
     bool res = recinit<Foo, Features...>::tinit(this); 
     //use res wisely 
    } 
}; 

tuo problema è che non si può scrivere specializzazioni parziali di funzioni, solo di classi/struct. E la struttura ausiliaria deve essere al di fuori di Foo oppure otterrà gli argomenti del modello dalla struttura che lo racchiude e che sarebbe male.

Non si dice ma presumo che init sia una funzione membro non statica. Se questo è il caso, gli argomenti args hanno poco senso: tutti dovrebbero essere this! Quindi, appena passato questa volta ed evitare il pacchetto negli argomenti. Ho provato a passare this come void* ma potrebbe essere problematico, quindi ho aggiunto un argomento modello aggiuntivo a recinit che sarà Foo.

E inoltre, ogni volta che si esegue un passaggio ricorsivo, ricordare di rimuovere un parametro.

+0

Anche se sono sicuro che tutte le risposte votate sono buone, questa è quella che più si adatta alle mie esigenze. L'ho collegato alla mia gerarchia e funziona perfettamente e rodrigo si è ritenuto corretto supporre che init() sia una funzione membro non statica. Ho imparato qualcosa di nuovo oggi, e anche un venerdì. Roba buona. –

+0

@AndyBrown: rileggendo la mia risposta, il 'static_cast' non è realmente necessario, perché' T' deve essere una classe base di 'F'. Rimosso, il codice è sempre più bello senza cast. – rodrigo

3

Forse si potrebbe provare qualcosa di simile:

template<typename... Features> 
struct Foo : Features... 
{ 
    bool init() 
    { 
     // Courtesy of Xeo :-) 
     auto il = {(static_cast<bool (Foo::*)()>(&Features::init))...}; 
     return std::all_of(il.begin(), il.end(), 
      [this] (bool (Foo::*f)()) { return (this->*f)(); } 
      ); 
    } 
}; 

Ecco un'alternativa, la versione più prolisso che utilizza i modelli variadic:

template<typename... Features> 
struct Foo : Features... 
{ 
    bool init() 
    { 
     return combine_and((&Features::init)...); 
    } 

private: 

    bool combine_and() 
    { 
     return true; 
    } 

    template<typename F> 
    bool combine_and(F f) 
    { 
     return (this->*f)(); 
    } 

    template<typename F1, typename... Fs> 
    bool combine_and(F1 f1, Fs... fs) 
    { 
     return ((this->*f1)() && combine_and(fs...)); 
    } 
}; 

Qualunque sia la soluzione che si sceglie, questo è come si potrebbe usare it:

#include <iostream> 

using namespace std; 

struct A { bool init() { cout << "Hello " << endl; return true; } }; 
struct B { bool init() { cout << "Template " << endl; return true; } }; 
struct C { bool init() { cout << "World!" << endl; return true; } }; 

int main() 
{ 
    Foo<A, B, C> f; 
    bool res = f.init(); // Prints "Hello Template World!" 
    cout << res; // Prints 1 
} 
+0

Questo non raccoglie tuttavia i valori di ritorno dalle funzioni 'init's. Dovrebbe essere più simile a 'fold ();'. – Xeo

+0

@Xeo: ho modificato la risposta. Questo mi sembra OK. –

+2

'auto il = {Caratteristiche :: init() ...}; return std :: all_of (il.begin(), il.end(), [] (bool b) {return b;}); ':) – Xeo

2

Il tuo codice ha due problemi. In primo luogo, l'piuttosto banale:

return recinit<F,G...>(args...); 

Hai già gestito F, lasciarlo fuori dalla lista di argomenti.

return recinit<G...>(args...); 

(Inoltre si dovrebbe probabilmente perfettamente avanzare degli argomenti.)

In secondo luogo, il codice non viene compilato perché il ricorsione ha un ancoraggio in fase di esecuzione ma non a tempo di compilazione. Cioè, il compilatore proverà a decomprimere l'argomento pack G ad infinitum. Per evitare ciò è necessario specializzare la funzione per un elenco di argomenti template vuoto.

Problemi correlati