Wenn ich kompiliere, schlägt es mit dem Fehler fehl:
Fehler C3520: ‘args’: Parameterpaket muss in diesem Zusammenhang erweitert werden
(in Funktion foo2).
Was, wenn Args war leer?
– RamblingMad
5. September 2014 um 7:28 Uhr
TC
Einer der Orte, an denen eine Paketerweiterung stattfinden kann, ist innerhalb von a geklammerte Init-Liste. Sie können dies ausnutzen, indem Sie die Erweiterung in die Initialisierungsliste eines Dummy-Arrays einfügen:
Um den Inhalt des Initialisierers näher zu erläutern:
{ 0, ( (void) bar(std::forward<Args>(args)), 0) ... };
| | | | |
| | | | --- pack expand the whole thing
| | | |
| | --perfect forwarding --- comma operator
| |
| -- cast to void to ensure that regardless of bar()'s return type
| the built-in comma operator is used rather than an overloaded one
|
---ensure that the array has at least one element so that we don't try to make an
illegal 0-length array when args is empty
Eine Variante, die ich gesehen habe, ist using expander = int[]; expander{...}; denn dann hat die Array-Variable keinen Namen und es wird für den Compiler offensichtlich, dass das Array nicht erstellt oder verwendet werden muss.
– Muhende Ente
5. September 2014 um 16:20 Uhr
Was ist das zweite 0 zum? Der direkt nach dem Komma-Operator
– Aaron McDaid
27. Juli 2015 um 21:20 Uhr
@AaronMcDaid Damit ist der Typ des gesamten Ausdrucks int und entspricht dem Elementtyp des Arrays.
– TC
27. Juli 2015 um 21:22 Uhr
Mir ist aufgefallen, dass man an schreiben kann operator void () (ja, verrückt, ich weiß). Aber nein, ich sage das Casting nicht wirklich void ist eine schlechte Idee. Es macht einfach Spaß, über verrückte Dinge nachzudenken, die passieren können, wenn man versucht, ein Paket mit minimalen Nebenwirkungen zu erweitern
– Aaron McDaid
27. Juli 2015 um 21:25 Uhr
@AaronMcDaid (void) ruft nie auf operator void() (Gott sei Dank).
– TC
27. Juli 2015 um 21:26 Uhr
Parameterpakete können nur in einer streng definierten Liste von Kontexten und Operatoren erweitert werden , ist keiner von ihnen. Mit anderen Worten, es ist nicht möglich, die Paketerweiterung zu verwenden, um einen Ausdruck zu generieren, der aus einer Reihe von Unterausdrücken besteht, die durch Operatoren getrennt sind ,.
Die Faustregel lautet: „Expansion kann a generieren aufführen von ,-getrennte Muster wo , ist ein aufführen Trennzeichen.” Operator , erstellt keine Liste im Sinne der Grammatik.
Um eine Funktion für jedes Argument aufzurufen, können Sie die Rekursion verwenden (das wichtigste Werkzeug in der Box des Variadic-Vorlagenprogrammierers):
Verdammt, du hast mich gerade darauf gewettet, dass ich darauf antworte schüttelt die Faust aber Sie sollten Ihrer Antwort wahrscheinlich eine perfekte Weiterleitung hinzufügen. das ist auch ein “primäres Werkzeug in der Box des Variadic-Template-Programmierers”.
– RamblingMad
5. September 2014 um 7:26 Uhr
Vielen Dank für Ihre Antwort. Ich kenne mich mit der Rekursionsimplementierung aus. Ich möchte nur eine Problemumgehung finden, um Code ohne Rekursion und neue Funktion zu kompilieren.
– Wjatscheslaw Dronow
5. September 2014 um 7:26 Uhr
@ViacheslavDronov, da Sie Vorlagen verwenden: Sie haben bereits eine Menge Funktionen, die vom Compiler generiert werden. Warum fügen Sie dieser Liste nicht eine hinzu?
– RamblingMad
5. September 2014 um 7:27 Uhr
@CoffeeandCode Ich erteile Ihnen offiziell die Erlaubnis, meine Antwort zu kopieren und sie durch Hinzufügen und Erklären der perfekten Weiterleitung zu erweitern.
– Angew ist nicht mehr stolz auf SO
5. September 2014 um 7:28 Uhr
Sie können die Erweiterung einfach mit einem Dummy-Array vornehmen. int dummy[] = { 0, ((void) bar(std::forward<Args>(args)),0)... };.
– TC
5. September 2014 um 10:15 Uhr
RamblingMad
SCHAMLOSE KOPIE [approved by its source]
Parameterpakete können nur in einer streng definierten Liste von Kontexten und Operatoren erweitert werden , ist keiner von ihnen. Mit anderen Worten, es ist nicht möglich, die Paketerweiterung zu verwenden, um einen Ausdruck zu generieren, der aus einer Reihe von Unterausdrücken besteht, die durch Operatoren getrennt sind ,.
Die Faustregel lautet: „Expansion kann eine Liste von generieren ,-getrennte Muster wo , ist ein Listentrennzeichen.” Operator , erstellt keine Liste im Sinne der Grammatik.
Um eine Funktion für jedes Argument aufzurufen, können Sie die Rekursion verwenden (das wichtigste Werkzeug in der Box des Variadic-Vorlagenprogrammierers):
Eine andere Sache, die Sie in dieser Antwort wahrscheinlich nicht gesehen haben, ist die Verwendung von && Spezifizierer und std::forward. In C++ ist die && Bezeichner kann eines von zwei Dingen bedeuten: rvalue-Referenzen oder universelle Referenzen.
Ich werde nicht auf rvalue-Referenzen eingehen, sondern auf jemanden, der mit variadischen Vorlagen arbeitet; universelle Referenzen sind ein Geschenk Gottes.
Perfekte Weiterleitung
Eine der Verwendungen von std::forward und universelle Referenzen sind eine perfekte Weiterleitung von Typen an andere Funktionen.
Wenn wir in Ihrem Beispiel eine übergeben int& zu foo2 es wird automatisch herabgestuft auf int wegen der Signatur der generierten foo2 Funktion nach Vorlagenabzug und wenn du wolltest dann diese weiterleiten arg zu einer anderen Funktion, die sie durch Referenz ändern würde, erhalten Sie unerwünschte Ergebnisse (die Variable wird nicht geändert), weil foo2 wird einen Verweis auf das temporäre übergeben, das durch Übergeben von an erstellt wurde int dazu. Um dies zu umgehen, geben wir eine Weiterleitungsfunktion an irgendein eine Art von Hinweis in eine Variable (rvalue oder lWert). Dann, um sicherzustellen, dass wir genau den Typ übergeben, der in der von uns verwendeten Weiterleitungsfunktion übergeben wird std::forwarddann und nur erlauben wir dann die Herabstufung von Typen; weil wir jetzt an dem Punkt sind, wo es am wichtigsten ist.
Sie können verwenden make_tuple für die Paketerweiterung, da es einen Kontext einführt, in dem die , Folge, die durch eine Erweiterung erzeugt wird, ist gültig
Was, wenn
Args
war leer?– RamblingMad
5. September 2014 um 7:28 Uhr