2013-08-22 4 views
10

Desidero chiedere se il seguente codice è valido.Espansioni multiple di più pacchetti di parametri nella stessa espressione

Mi chiedo se sia possibile espandere i pacchetti di parametri più volte in un'unica espressione.

#include <iostream> 
#include <tuple> 

class ExpandWithConstructor 
{ 
    public: 
     template <typename ... T> 
     ExpandWithConstructor(T... args) { } 
}; 

template <typename T> 
int PrintArgs(T arg) 
{ 
    std::cout << arg << ", "; 
    return 0; 
} 

template <typename Head, typename ... T> 
class DebugPrinter: public DebugPrinter<T...> 
{ 
    public: 
     DebugPrinter() { } 

     template< typename ...Y> 
     DebugPrinter(Y ... rest) 
     { 
      std::cout << "Construction of: " << __PRETTY_FUNCTION__ << " Values: " ; 
      ExpandWithConstructor{PrintArgs(rest)...}; 
      std::cout << std::endl; 
     } 

}; 

template <typename Head> 
class DebugPrinter<Head> 
{ 
    public: 
}; 

template <typename ... T> 
class TypeContainer: public std::tuple<T...> 
{ 
    public: 
     TypeContainer(T... args):std::tuple<T...>(args...){}; 
}; 

template <typename... T1> class CheckVariadic; 

template <typename... T1, typename ...T2> 
class CheckVariadic< TypeContainer<T1...>, TypeContainer<T2...>> : 
      public DebugPrinter< T1, T2, T1...>... 
{ 
    public: 
     CheckVariadic(T1... args1, T2... args2, T1... args3): DebugPrinter< T1, T2, T1...>(args1, args2..., args1)... {} 
}; 


int main() 
{ 
    CheckVariadic< TypeContainer<int,float>, TypeContainer<char, void*>> checkVariadic1{ 1,2.2,'c',(void*)0xddddd,5,6.6,}; 
} 

Come si può vedere il codice utilizza: DebugPrinter < T1, T2, T1 ...> ...

se T1 è dato con "int, float" e T2 è "char, void *" che si espande

DebugPrinter< T1, T2, int, float>... 

che si espande

DebugPrinter< int, char, int, float> 
DebugPrinter< float, void*, int, float> 

La stessa espansione va con:

DebugPrinter< T1, T2, T1...>(args1, args2..., args1)... 

il codice viene compilato con clang3.3 ma NON con gcc4.8.1 quindi voglio chiedere se il codice è valido o meno.

Aggiornamento: gcc 7.2 ancora non ha compilato il codice.

+1

posso farlo compilare in g ++ 4.7 utilizzando 'DebugPrinter {args1, args2 ..., args1} ... ', ma l'uscita è nella direzione opposta di clang. – kennytm

+2

L'ordine inverso delle chiamate di funzione da un elenco di inizializzazione del costruttore è un bug noto in g ++. Posso anche compilare il codice con g ++ 4.7.2. Ma g ++ 4.8.1 non riesce ancora. – Klaus

+1

@KennyTM: per essere precisi, per le funzioni regolari l'ordine di valutazione degli argomenti non è specificato; storicamente g ++ ha usato l'ordine da destra a sinistra. Per una lista di inizializzazione, credo che l'ordine sia specificato e g ++ semplicemente non è riuscito ad aggiornarlo ancora. –

risposta

3

Sì, il tuo codice è perfettamente valido. L'espansione di un pacchetto è composta da un motivo e da un'ellissi e può apparire in un modello di espansione di un altro pacchetto. Nel paragrafo § 14.5.3/5 della norma si trova:

[...] L'aspetto del nome di un pacchetto di parametri viene ampliato solo dall'espansione del pacchetto di chiusura più interno. Il modello di espansione di un pacchetto deve nominare uno o più pacchetti di parametri che non sono espansi da un'espansione di pacchetti nidificati; [...]

Un'espansione di pacchetto può essere utilizzata in qualsiasi contesto menzionato in §14.5.3/4. Dato il tuo esempio:

DebugPrinter< T1, T2, T1...>... 

Entrambe le espansioni del pacchetto sono valide. Il contesto del primo è uno template-argument-list mentre il secondo viene visualizzato in un base-specifier-list.

L'esempio fornito dal testo standard:

template<class ... Args> 
void g(Args ... args) {     // OK: Args is expanded by the function 
              // parameter pack args 
    f(const_cast<const Args*>(&args)...); // OK: “Args” and “args” are expanded 
    f(5 ...);        // error: pattern does not contain any 
              // parameter packs 
    f(args);        // error: parameter pack “args” is not 
              // expanded 
    f(h(args ...) + args ...);   // OK: first “args” expanded within h, 
              // second “args” expanded within f 
} 
+3

che dire di 'f (args) ...;'? – gnzlbg