Freigeben über


Auslassungszeichen- und Variadic-Vorlagen

In diesem Artikel wird gezeigt, wie die Auslassungspunkte (...) mit variadic Vorlagen von C++ verwendet werden. Das Auslassungszeichen hatte viele Verwendungszwecke in C und C++. Hierzu gehören Variablenargumentlisten für Funktionen. Die printf()-Funktion der C-Laufzeitbibliothek ist eines der bekanntesten Beispiele.

Eine variadic-Vorlage ist eine Klassen- oder Funktionsvorlage, die eine beliebige Anzahl von Argumenten unterstützt. Dieser Mechanismus ist für C++-Bibliotheksentwickler besonders nützlich, da Sie ihn auf Klassen- und Funktionsvorlagen anwenden können, und dadurch eine große Bandbreite typsicherer und nicht trivialer Funktionalität und Flexibilität bereitstellen können.

Syntax

Ein Auslassungszeichen wird auf zwei Arten von variadic-Vorlagen verwendet. Auf der linken Seite des Parameternamens zeigt es ein Parameterpaket an und auf der rechten Seite des Parameternamens erweitert es die Parameterpakete in separaten Namen.

Es folgt ein einfaches Beispiel für die Definitionssyntax von variadic-Vorlagenklassen:

template<typename... Arguments> class classname;

Sie können für Parameterpakete und Erweiterungen Leerstellen um die Auslassungszeichen entsprechend den jeweiligen Anforderungen, wie in den folgenden Beispielen dargestellt, hinzufügen:

template<typename ...Arguments> class classname;

Oder:

template<typename ... Arguments> class classname;

Beachten Sie, dass in diesem Artikel die Konvention verwendet wird, die im ersten Beispiel gezeigt wird (die Auslassungszeichen werden an typename angefügt).

In den vorherigen Beispielen ist Arguments ein Parameterpaket. Die classname-Klasse kann eine variable Anzahl von Argumenten akzeptieren, wie in den folgenden Beispielen gezeigt.

template<typename... Arguments> class vtclass;

vtclass< > vtinstance1;
vtclass<int> vtinstance2;
vtclass<float, bool> vtinstance3;
vtclass<long, std::vector<int>, std::string> vtinstance4;

Mit einer variadic-Vorlagenklassendefinition können Sie auch mindestens einen Parameter anfordern:

template <typename First, typename... Rest> class classname; 

Es folgt ein einfaches Beispiel für die Syntax der variadic-Vorlagenfunktion:

template <typename... Arguments> returntype functionname(Arguments... args);

Das Arguments-Parameterpaket wird dann zur Verwendung erweitert, wie im nächsten Abschnitt variadic-Vorlagen gezeigt.

Andere Formen der variadic-Vorlagenfunktionssyntax sind möglich, darunter diese Beispiele:

template <typename... Arguments> returntype functionname(Arguments&... args); 
template <typename... Arguments> returntype functionname(Arguments&&... args);
template <typename... Arguments> returntype functionname(Arguments*... args);

Bezeichner wie const sind ebenfalls zulässig:

template <typename... Arguments> returntype functionname(const Arguments&... args); 

Wie bei variadic-Vorlagenklassendefinitionen können Sie Funktionen ausführen, die mindestens einen Parameter erfordern:

template <typename First, typename... Rest> returntype functionname(const First& first, const Rest&... args); 

Variadic-Vorlagen verwenden den Operator sizeof...() (nicht verknüpft mit dem älteren sizeof()-Operator):

template<typename... Arguments>
void tfunc(const Arguments&... args)
{
    const unsigned numargs = sizeof...(Arguments);

    X xobj[numargs]; // array of some previously defined type X

    helper_func(xobj, args...);
}

Weitere Informationen zur Platzierung von Auslassungszeichen

In diesem Artikel wurde bereits die Platzierung des Auslassungszeichens, mit der Parameterpakete und Erweiterungen definiert werden, wie folgt beschrieben: "Auf der linken Seite des Parameternamens zeigt es ein Parameterpaket an, und auf der rechten Seite des Parameternamens erweitert es die Parameterpakete in separaten Namen". Dies ist technisch gesehen richtig, kann jedoch bei der Übersetzung in Code verwirrend sein. Betrachten Sie das folgende Beispiel:

  • In einer template-parameter-list (template <parameter-list>), führt typename... ein Vorlagenparameterpaket ein.

  • In einer parameter-declaration-clause (func(parameter-list)) führt ein Auslassungszeichen "der obersten Ebene" ein Funktionsparameterpaket ein. Dabei ist die Positionierung des Auslassungszeichens wichtig:

    // v1 is NOT a function parameter pack:
    template <typename... Types> void func1(std::vector<Types...> v1); 
    
    // v2 IS a function parameter pack:
    template <typename... Types> void func2(std::vector<Types>... v2); 
    
  • Wenn das Auslassungszeichen direkt nach dem Parameternamen angezeigt wird, haben Sie eine Parameterpaketerweiterung.

Beispiel

Eine gute Möglichkeit, den Funktionsmechanismus von variadic-Vorlagen zu veranschaulichen, ist dessen Verwendung in der Neuschreibung einiger Funktionen von printf:

#include <iostream>

using namespace std;

void print() {
    cout << endl;
}

template <typename T> void print(const T& t) {
    cout << t << endl;
}

template <typename First, typename... Rest> void print(const First& first, const Rest&... rest) {
    cout << first << ", ";
    print(rest...); // recursive call using pack expansion syntax
}

int main()
{
    print(); // calls first overload, outputting only a newline
    print(1); // calls second overload
    
    // these call the third overload, the variadic template, 
    // which uses recursion as needed.
    print(10, 20);
    print(100, 200, 300);
    print("first", 2, "third", 3.14159);
}

Ausgabe

1
10, 20
100, 200, 300
first, 2, third, 3.14159

Hinweis

Die meisten Implementierungen, die variadic-Vorlagenfunktionen enthalten, verwenden eine Form von Rekursion, die sich jedoch leicht von herkömmlicher Rekursion unterscheidet. Herkömmliche Rekursion umfasst eine Funktion, die sich selbst mit der gleichen Signatur aufruft. (Sie ist überladen oder vorlagenbasiert, aber dieselbe Signatur wird jedes Mal ausgewählt.) Variadic-Rekursion beinhaltet den Aufruf einer variadic-Funktionsvorlage, indem eine unterschiedliche (fast immer abnehmende) Anzahl von Argumenten verwendet und somit verhindert wird, dass sich die Signatur jedes Mal ändert.Eine "Basisfall" ist dennoch erforderlich, aber die Art der Rekursion ist anders.

Siehe auch

Referenz

Auslassungszeichen (...)