2012-04-06 11 views
19

Ich kenne bereits die stdarg.h Weg, um eine Funktion mit variablen Argumenten in C++ wie here zum Beispiel diskutiert. Ich weiß auch C++ 11 Standard hat variadic Vorlagen wie erklärt here.Eine Funktion mit variabler Anzahl von Argumenten mit bekannten Typen, die C++ 11-Wege

Aber in beiden oben genannten Schemata wissen wir nicht (und wir können nicht erzwingen) Argumenttypen in der Kompilierungszeit afaik. Was ich suche ist, variable Argumente von bekannten Typen zu einer Funktion zu übergeben. Ich denke, dass dies getan werden kann, weil ich über sie gelesen here:

Variadische Vorlagen, die auch Funktionen zum Erstellen verwendet werden können, die eine variable Anzahl von Argumenten entgegennehmen, sind oft die bessere Wahl, da sie keine Beschränkungen auferlegt werden die Arten von Argumenten, führen keine Integral- und Gleitkomma-Aktionen durch und sind typsicher.

Ist es möglich? Wenn ja, wie kann ich das tun?

Antwort

30

Es ist einfach, eine Funktion mit variadischen Vorlagen zu schreiben, die eine beliebige Anzahl von Argumenten akzeptieren. Der einzige Unterschied zum allgemeinen Muster besteht darin, dass anstelle eines Template-Parameters ein konkreter Typ als erstes Argument (Kopf) verwendet wird. Das folgende Beispiel zeigt eine Funktion foobar, die eine beliebige Anzahl von Strings akzeptiert.

// used for end of recursion - and for the empty arguments list 
void foobar() { } 

template <typename ...Tail> 
void foobar(const std::string& head, Tail&&... tail) 
{ 
    // do something with head 
    std::cout << head << '\n'; 
    // call foobar recursively with remaining arguments 
    foobar(std::forward<Tail>(tail)...); 
} 

foobar("Hello", "World", "..."); 

Ich persönlich ziehe std::initializer_list statt variadische Vorlagen. Weil variadische Vorlagen komplexer sind und zusätzliche Erfahrung erfordern. Mit std::initializer_list, könnte es so aussehen:

void foobar(std::initializer_list<std::string> values) 
{ 
    for (auto& value : values) { 
     // do something with value 
     std::cout << value << '\n'; 
    } 
} 

foobar({ "Hello", "World", "...", }); 

Leider sind die zusätzlichen geschweiften Klammern sind erforderlich, wenn std::initializer_list mit regulären Funktionen. Sie werden nicht für Konstruktoren benötigt, wenn die neue Initialisierungssyntax verwendet wird.

Bearbeiten: Rewrote die Antwort entsprechend der Rückmeldung. Insbesondere habe ich die Reihenfolge der beiden Lösungen/Beispiele geändert.

+1

Ich denke, es ging nur um mein Missverständnis. Ich kannte den zweiten Weg, aber ich dachte immer, es gäbe keine Kontrolle über die Art der Parameter. Das ist nicht wahr. Da die Funktion einen ersten Parameter eines bekannten Typs (String hier) verwendet, wird sie implizit gezwungen, ihre Parameter dieses Typs zu haben. Vielen Dank. – melmi

+2

Sie möchten vielleicht zuerst die zweite Code-Box verschieben. Es ist eine sehr gute Lösung für das Problem, während die 'initializer_list' Version nicht ist. Ich wollte eine große, komplexe Sache mit SFINAE und so veröffentlichen, aber das ist viel vernünftiger. –

+0

@nosid Sie können tatsächlich geschweifte Klammern vermeiden, wenn Sie sowohl die std :: initializer_list als auch die variadic-Vorlagen-Methode mischen, indem Sie einen variadischen Template-Wrapper um die std :: initializer_list-Version erstellen, wie folgt: [siehe on coliru] (http: //coliru.stacked-crooked.com/a/4baef67192a0310c). –

2

Wenn variable Parameter alle von einem Typ sind, können Sie die Funktionssignatur ändern, um ein Array dieser Typen anstelle von '...' zu verwenden.

+0

Ein Array eines Typs benötigt eine Iteration, während die Verwendung variadischer Funktionen einen Inline-Mechanismus zur Kompilierung bietet. Sie können sagen, dass Schleifen in einem Array nicht so zeitaufwendig ist, aber betrachten Sie es in einem anderen mehrere große Schleifen. – melmi

Verwandte Themen