2014-12-09 6 views
15

Qual è il modo più semplice per stampare un pacchetto di parametri, separati da virgole, utilizzando std::ostream?Qual è il modo più semplice per stampare un pacchetto di parametri variadic usando std :: ostream?

Esempio:

template<typename... Args> 
void doPrint(std::ostream& out, Args... args){ 
    out << args...; // WRONG! What to write here? 
} 

// Usage: 
int main(){ 
    doPrint(std::cout,34,"bla",15); // Should print: 34,bla,15 
} 

Nota: Si può supporre che un corrispondente overload dell'operatore << è disponibile per tutti i tipi di parametro confezione.

+1

In C++ 17, dovresti dire '(out << ... <<< args);'. –

+0

Nota: Questo non è un duplicato - 'foo << X << Y;' generalmente non è uguale a 'foo << X; foo << Y; 'specialmente quando' foo' ha effetti collaterali come aprire un file su disco. – MSalters

+0

@MSalters anche, 'foo << X << Y' non specifica l'ordine di valutazione di' X' e 'Y' (prima di C++ 17, cioè) –

risposta

17

Un'altra alternativa senza chiamate ricorsive e le virgole dove si voleva:

template <typename Arg, typename... Args> 
void doPrint(std::ostream& out, Arg&& arg, Args&&... args) 
{ 
    out << std::forward<Arg>(arg); 
    using expander = int[]; 
    (void)expander{0, (void(out << ',' << std::forward<Args>(args)),0)...}; 
} 

DEMO

+1

Questo è il più facile. È solo che sembra strano, hehe. –

+4

@ GermánDiago http://en.cppreference.com/w/cpp/language/parameter_pack fornisce un esempio simile: 'int dummy [sizeof ... (Ts)] = {(std :: cout << args, 0) ...}; ' –

+2

Come nota a margine, puoi saltare la dichiarazione' using' e scrivere semplicemente '(void) (int []) {(std :: cout << ',' << std :: forward (args)), 0) ...}; '. Inoltre non hai bisogno del primo zero nelle parentesi graffe o del cast "void" interno. –

4

La risposta usuale è quello di definire due sovraccarichi separati, con uno vuoto per il caso base:

Naturalmente nel codice reale che non avrebbe copie degli argomenti ogni volta e utilizzare invece inoltro riferimenti, ma tu hai l'idea.

Se si desidera aggiungere virgole dopo ogni elemento, anche dopo l'ultimo, è sufficiente sostituire out << t con out << t << ','.

Se si desidera solo virgole all'interno, non oltre l'ultimo elemento, è necessario un sovraccarico a un argomento separato che non stampa la virgola e un sovraccarico generico prende due distinti argomenti prima del pacchetto, ad esempio:

template <typename T> 
void doPrint(std::ostream& out, T t) 
{ 
    out << t; 
} 

template <typename T, typename U, typename... Args> 
void doPrint(std::ostream& out, T t, U u, Args... args) 
{ 
    out << t << ','; 
    doPrint(out, u, args...); 
} 
+0

Puoi elaborare un po 'sui riferimenti di inoltro?Non sono troppo fermo in quella roba in C++ 11, e copiare i parametri in giro sembra davvero eccessivo. – gexicide

+0

@gexicide: migliaia di duplicati su questo sito, basta cercare in giro. Stai cercando 'Args && ... args' e' std :: forward (args) ... '. –

4

L'espansione del pacchetto di parametri funziona solo nelle chiamate di funzione normale, non per gli operatori di infisso. Quindi, è necessario convertire s << x sintassi in funzione pianura chiamata sintassi f(s, x):

template<class Head> 
void print_args_(std::ostream& s, Head&& head) { 
    s << std::forward<Head>(head); 
} 

template<class Head, class... Tail> 
void print_args_(std::ostream& s, Head&& head, Tail&&... tail) { 
    s << std::forward<Head>(head); 
    print_args_(s, std::forward<Tail>(tail)...); 
} 

template<class... Args> 
void print_args(Args&&... args) { 
    print_args_(std::cout, std::forward<Args>(args)...); 
} 
2

In C++ 17, ci sarà un modo più semplice (come suggerito da Kerrek SB nei commenti, questo era effettivamente presente in N4606, la prima bozza post-C++ 14), chiamata fold expressions:

Il codice sarebbe:

(out << ... << args); 

e il modello expression op...op parameter-pack è chiamato binario sinistro piega, la cui definizione è equivalente a (((expressionop arg1) op arg2) op arg3) .... op argN.

ritengo le parentesi esterne non sono strettamente necessari per un'espressione-dichiarazione come questa, ma se l'espressione piega è un operando di un altro operatore allora o richiesti, o una buona idea :)