2014-11-05 29 views
36

Qual è la differenza tra le seguenti 3 chiamate per la funzione gun?Espansione con modelli variadici

template <class... Ts> void fun(Ts... vs) { 
    gun(A<Ts...>::hun(vs)...); 
    gun(A<Ts...>::hun(vs...)); 
    gun(A<Ts>::hun(vs)...); 
} 

Sono interessato a una risposta che spiega le tre chiamate utilizzando un esempio specifico.

+9

Divertimento con le pistole ?? – 0x499602D2

+4

@ 0x499602D2 Fun gun, hun – chbaker0

+0

Una domanda simile è questa http://stackoverflow.com/questions/17652412/what-are-the-rules-for-the-token-in-the-context-of-variadic- modello, ma le risposte qui sotto chiariscono in modo più approfondito la mia domanda, e sono più specifiche –

risposta

57

Originariamente ho semplicemente risposto alla domanda, ma volevo ampliare questo aspetto per fornire una spiegazione più completa di come i pacchetti vengono espansi in cosa. Questo è il modo in cui penso comunque alle cose.

Qualsiasi pacchetto immediatamente seguito da un'ellissi è appena espanso. Quindi A<Ts...> equivale a A<T1, T2, ..., TN> e hun(vs...) equivale allo stesso modo a hun(v1, v2, ..., vn). Dove diventa complicato è quando invece di un pacchetto seguito da ellissi ottieni qualcosa come ((expr)...). Questo verrà espanso in (expr1, expr2, ..., exprN) dove expri si riferisce all'espressione originale con qualsiasi pacchetto sostituito con la versione i di esso. Quindi se avevi hun((vs+1)...), questo diventa hun(v1+1, v2+1, ..., vn+1). Dove diventa più divertente è che expr può contenere più di un pacco (purché abbiano tutti la stessa dimensione!). Questo è il modo in cui implementiamo il modello di inoltro perfetto standard;

foo(std::forward<Args>(args)...) 

Qui expr contiene due confezioni (Args e args sono entrambi confezioni) e l'espansione "itera" oltre entrambi:

foo(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), ..., std::forward<ArgN>(argN)); 

Tale ragionamento dovrebbe rendere possibile camminare rapidamente attraverso i vostri casi per, dì, cosa succede quando chiami foo(1, 2, '3').

Il primo, gun(A<Ts...>::hun(vs)...); espande Ts "in luogo" e poi c'è un'espressione per espandere negli ultimi ellissi, quindi ciò richiede:

gun(A<int, int, char>::hun(1), 
    A<int, int, char>::hun(2), 
    A<int, int, char>::hun('3')); 

Il secondo, gun(A<Ts...>::hun(vs...)); espande entrambi i pacchetti in luogo:

gun(A<int, int, char>::hun(1, 2, '3')); 

il terzo, gun(A<Ts>::hun(vs)...), espande entrambi i pacchetti contemporaneamente:

gun(A<int>::hun(1), 
    A<int>::hun(2), 
    A<char>::hun('3')); 

[update] Per completezza, gun(A<Ts>::hun(vs...)...) chiamerebbe:

gun(A<int>::hun(1, 2, '3'), 
    A<int>::hun(1, 2, '3'), 
    A<char>::hun(1, 2, '3')); 

Infine, c'è un ultimo caso di prendere in considerazione dove andiamo a mare sui puntini di sospensione:

gun(A<Ts...>::hun(vs...)...); 

Questo non verrà compilato. Espandiamo sia Ts sia vs "in posizione", ma non abbiamo ancora alcun pacchetto da espandere per gli ellissi finali.

16

Ecco come si espandono quando Ts è T, U e VS è t, u:

gun(A<Ts...>::hun(vs)...) -> gun(A<T, U>::hun(t), A<T, U>::hun(u)) 
gun(A<Ts...>::hun(vs...)) -> gun(A<T, U>::hun(t, u)); 
gun(A<Ts>::hun(vs)...) -> gun(A<T>::hun(t), A<U>::hun(u)) 

E un altro caso in cui non copriva:

gun(A<Ts>::hun(vs...)...) -> gun(A<T>::hun(t, u), A<U>::hun(t, u)) 

Se si esegue il codice di seguito in vs14 si otterrà questa uscita:

calling gun(A<Ts...>::hun(vs)...); 
    struct A<int,double>::hun(double); 
    struct A<int,double>::hun(int); 
    gun(struct A<int,double>, struct A<int,double>); 
calling gun(A<Ts...>::hun(vs...)); 
    struct A<int,double>::hun(int, double); 
    gun(struct A<int,double>); 
calling gun(A<Ts>::hun(vs)...); 
    struct A<double>::hun(double); 
    struct A<int>::hun(int); 
    gun(struct A<int>, struct A<double>); 
calling gun(A<Ts>::hun(vs...)...); 
    struct A<double>::hun(int, double); 
    struct A<int>::hun(int, double); 
    gun(struct A<int>, struct A<double>); 

Codice:

#include <iostream> 
#include <typeinfo> 

using namespace std; 

void printTypes() {} 

template<typename T, typename... Ts> void printTypes(T, Ts... vs) { 
    cout << typeid(T).name() << (sizeof...(Ts) ? ", " : ""); 
    printTypes(vs...); 
} 

template<typename... Ts> struct A { 
    template<typename... Us> 
    static auto hun(Us... vs) { 
     cout << " " << typeid(A).name() << "::hun("; 
     printTypes(vs...); 
     cout << ");" << endl; 
     return A{}; 
    } 
}; 

template<typename... Ts> void gun(Ts... vs) { 
    cout << " gun("; 
    printTypes(vs...); 
    cout << ");" << endl; 
} 

template<typename... Ts> void fun(Ts... vs) { 
    cout << "calling gun(A<Ts...>::hun(vs)...);" << endl; 
    gun(A<Ts...>::hun(vs)...); 
    cout << "calling gun(A<Ts...>::hun(vs...));" << endl; 
    gun(A<Ts...>::hun(vs...)); 
    cout << "calling gun(A<Ts>::hun(vs)...);" << endl; 
    gun(A<Ts>::hun(vs)...); 
    cout << "calling gun(A<Ts>::hun(vs...)...);" << endl; 
    gun(A<Ts>::hun(vs...)...); 
} 

int main() { 
    fun(1, 2.0); 
} 
+0

Grazie mille per la risposta e per l'esempio di codice! Tuttavia, ho scelto la risposta precedente in quanto ha fornito maggiori dettagli. –

Problemi correlati