Betrachten Sie den Fall einer Vorlagenfunktion mit variablen Vorlagenargumenten:
template<typename Tret, typename... T> Tret func(const T&... t);
Jetzt habe ich ein Tupel t
von Werten. Wie rufe ich func()
mit den Tupelwerten als Argumente auf? Ich habe über das bind()
Funktionsobjekt mit call()
Funktion und auch über die apply()
Funktion in verschiedenen inzwischen veralteten Dokumenten gelesen . Die Implementierung von GNU GCC 4.4 scheint eine call()
Funktion in der bind()
Klasse zu haben, aber es gibt nur sehr wenig Dokumentation zu diesem Thema.
Einige Leute schlagen handgeschriebene rekursive Hacks vor, aber der wahre Wert variadischer Vorlagenargumente besteht darin, sie in Fällen wie oben verwenden zu können.
Hat jemand eine Lösung dafür oder einen Hinweis darauf, wo man darüber lesen kann?
integer_sequence
, siehe en.cppreference.com/w/cpp/utility/integer_sequenceinteger_sequence S
, rufen Sie einfach Ihre Funktion als auffunc(std::get<S>(tuple)...)
und lassen den Compiler den Rest erledigen.Antworten:
Hier ist mein Code, wenn jemand interessiert ist
Grundsätzlich rollt der Compiler zur Kompilierungszeit rekursiv alle Argumente in verschiedenen Inklusivfunktionsaufrufen ab. <N> -> ruft <N-1> -> Aufrufe auf ... -> ruft <0> auf. Dies ist der letzte und der Compiler optimiert Die verschiedenen Zwischenfunktionsaufrufe behalten nur den letzten bei, der func entspricht (arg1, arg2, arg3, ...).
Es werden 2 Versionen bereitgestellt, eine für eine Funktion, die für ein Objekt aufgerufen wird, und die andere für eine statische Funktion.
quelle
tr1
Zeug kann jetzt mit c ++ 11 herausgenommen werdenIn C ++ 17 können Sie dies tun:
Dies funktioniert bereits in Clang ++ 3.9 mit std :: experimentelle :: apply.
Als Antwort auf den Kommentar, dass dies nicht funktioniert, wenn
the_function
eine Vorlage erstellt wird, wird Folgendes umgangen:Diese Problemumgehung ist eine vereinfachte Lösung für das allgemeine Problem, Überlastsätze und Funktionsvorlagen zu übergeben, bei denen eine Funktion erwartet wird. Die allgemeine Lösung (eine, die sich um perfekte Weiterleitung, Konsequenz und Nichtausnahme kümmert) wird hier vorgestellt: https://blog.tartanllama.xyz/passing-overload-sets/ .
quelle
the_function
es als Vorlage verwendet wird.std::apply(add_generic<float>, std::make_pair(2.0f, 3.0f));
In C ++ gibt es viele Möglichkeiten, Tupel zu erweitern / zu entpacken und diese Tupelelemente auf eine variable Vorlagenfunktion anzuwenden. Hier ist eine kleine Hilfsklasse, die ein Indexarray erstellt. Es wird häufig in der Metaprogrammierung von Vorlagen verwendet:
Jetzt ist der Code, der die Arbeit erledigt, nicht so groß:
Der Test ist unten dargestellt:
Ich bin kein großer Experte für andere Sprachen, aber ich denke, wenn diese Sprachen keine solche Funktionalität in ihrem Menü haben, gibt es keine Möglichkeit, dies zu tun. Zumindest mit C ++ können Sie, und ich denke, es ist nicht so kompliziert ...
quelle
template<class ... T> void three(T...) {}
und versuche, es anzuwenden, wird es nicht kompiliert.Ich finde, dass dies die eleganteste Lösung ist (und sie wird optimal weitergeleitet):
Anwendungsbeispiel:
Leider kann GCC (mindestens 4.6) dies nicht mit "sorry, nicht implementiert: Mangling Overload" kompilieren (was einfach bedeutet, dass der Compiler die C ++ 11-Spezifikation noch nicht vollständig implementiert hat), und da er verschiedene Vorlagen verwendet, wird dies nicht der Fall sein Arbeit in MSVC, so ist es mehr oder weniger nutzlos. Sobald es jedoch einen Compiler gibt, der die Spezifikation unterstützt, ist dies meiner Meinung nach der beste Ansatz. (Hinweis: Es ist nicht so schwer, dies zu ändern, damit Sie die Mängel in GCC umgehen oder mit Boost Preprocessor implementieren können, aber es ruiniert die Eleganz, daher ist dies die Version, die ich veröffentliche.)GCC 4.7 unterstützt diesen Code jetzt einwandfrei.
Bearbeiten: Vorwärts um den tatsächlichen Funktionsaufruf hinzugefügt, um das r-Wert-Referenzformular * zu unterstützen, falls Sie clang verwenden (oder wenn jemand anderes tatsächlich dazu kommt, es hinzuzufügen).
Bearbeiten: Es wurde ein fehlender Vorlauf um das Funktionsobjekt im Hauptteil der Nicht-Mitglied-Apply-Funktion hinzugefügt. Vielen Dank an pheedbaq für den Hinweis, dass es fehlte.
Edit: Und hier ist die C ++ 14-Version, nur weil sie so viel schöner ist (noch nicht kompiliert):
Hier ist eine Version für Mitgliedsfunktionen (nicht sehr getestet!):
quelle
apply
Warum wird in der Nichtmitgliedsfunktion aus Neugierf
nicht mit einemstd::forward
Aufruf umgebrochen, wie dies beim Rückgabetyp der Fall ist? Wird es nicht benötigt?foo('x', true)
kompilieren , und genau den gleichen Assembler-Code kompiliert wieapply(foo, ::std::make_tuple('x', true))
bei jeder anderen Optimierungsstufe als -O0.integer_sequence
Sie sogar eine fast korrekte Implementierungapply()
in seinem Beispiel. siehe meine Antwort unten.Dies wird aus dem C ++ 14-Entwurf mit index_sequence angepasst. Ich könnte vorschlagen, mich in einem zukünftigen Standard (TS) zu bewerben.
quelle
Die Nachrichten sehen nicht gut aus.
Nachdem ich den gerade veröffentlichten Standardentwurf gelesen habe , sehe ich keine eingebaute Lösung dafür, was seltsam erscheint.
Der beste Ort, um nach solchen Dingen zu fragen (falls Sie dies noch nicht getan haben), ist comp.lang.c ++. Moderiert, da einige Leute dort regelmäßig an der Erstellung des Standardposts beteiligt sind.
Wenn Sie sich diesen Thread ansehen, hat jemand die gleiche Frage (vielleicht sind Sie es, in diesem Fall finden Sie diese ganze Antwort ein wenig frustrierend!), Und es werden einige hässliche Implementierungen vorgeschlagen.
Ich habe mich nur gefragt, ob es einfacher wäre, die Funktion a akzeptieren zu lassen
tuple
, da die Konvertierung auf diese Weise einfacher ist. Dies impliziert jedoch, dass alle Funktionen Tupel als Argumente für maximale Flexibilität akzeptieren sollten, und dies zeigt nur die Seltsamkeit, keine integrierte Erweiterung von Tupel zu Funktionsargumentpaket bereitzustellen.Update: Der obige Link funktioniert nicht - versuchen Sie Folgendes einzufügen:
http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/750fa3815cdaac45/d8dc09e34bbb9661?lnk=gst&q=tuple+variadic#d8dc09e34bbb9661
quelle
Alle diese Implementierungen sind gut. Aufgrund der Verwendung des Zeigers auf den Elementfunktions-Compiler kann der Zielfunktionsaufruf jedoch häufig nicht inline geschaltet werden (zumindest gcc 4.8 kann dies nicht, egal was passiert. Warum kann gcc keine bestimmbaren Funktionszeiger inline schalten? ).
Die Dinge ändern sich jedoch, wenn der Zeiger auf die Elementfunktion als Vorlagenargumente und nicht als Funktionsparameter gesendet wird:
Und ussage:
Nachweis von inlinable http://goo.gl/5UqVnC
Mit kleinen Änderungen können wir "überladen"
apply_tuple
:Außerdem ist dies die einzige Lösung, die mit Vorlagenfunktionen funktioniert.
quelle
1) Wenn Sie eine vorgefertigte parameter_pack-Struktur als Funktionsargument haben, können Sie std :: tie einfach wie folgt verwenden:
2) Wenn Sie kein fertiges Parampack-Argument haben, müssen Sie das Tupel so abwickeln
quelle
Wie wäre es damit:
Die
run_tuple
Funktionsvorlage nimmt das angegebene Tupel und übergibt seine Elemente einzeln an die angegebene Funktion. Es führt seine Arbeit aus, indem es seine Hilfsfunktionsvorlagen rekursiv aufruftexplode_tuple
. Es ist wichtig, dassrun_tuple
die Größe des Tupels an übergeben wirdexplode_tuple
. Diese Zahl dient als Zähler für die Anzahl der zu extrahierenden Elemente.Wenn das Tupel leer ist, wird
run_tuple
die erste Version vonexplode_tuple
mit der Remote-Funktion als einzigem anderen Argument aufgerufen. Die Remote-Funktion wird ohne Argumente aufgerufen und wir sind fertig. Wenn das Tupel nicht leer ist, wird eine höhere Nummerexplode_tuple
zusammen mit der Remote-Funktion an die zweite Version von übergeben . Ein rekursiver Aufruf anexplode_tuple
wird mit denselben Argumenten erstellt, außer dass die Zählernummer um eins verringert wird und (ein Verweis auf) das letzte Tupelelement als Argument nach der Remote-Funktion angeheftet wird. Bei einem rekursiven Aufruf ist entweder der Zähler nicht Null, und ein weiterer Aufruf wird ausgeführt, wobei der Zähler wieder verringert wird und das nächste nicht referenzierte Element nach der Remote-Funktion, jedoch vor den anderen eingefügten Argumenten, in die Argumentliste eingefügt wird, oder der Zähler erreicht Null und die Remote-Funktion wird mit allen danach akkumulierten Argumenten aufgerufen .Ich bin nicht sicher, ob ich die Syntax habe, eine bestimmte Version einer Funktionsvorlage zu erzwingen. Ich denke, Sie können einen Zeiger auf die Funktion als Funktionsobjekt verwenden. Der Compiler wird das Problem automatisch beheben.
quelle
Ich evaluiere MSVS 2013RC und es konnte in einigen Fällen einige der hier vorgeschlagenen vorherigen Lösungen nicht kompilieren. Zum Beispiel kann MSVS "auto" -Rückgaben nicht kompilieren, wenn zu viele Funktionsparameter vorhanden sind, da die Namespace-Imbrikation begrenzt ist (ich habe diese Informationen an Microsoft gesendet, um sie korrigieren zu lassen). In anderen Fällen benötigen wir Zugriff auf die Rückgabe der Funktion, obwohl dies auch mit einer Lamda möglich ist: Die folgenden beiden Beispiele liefern das gleiche Ergebnis.
Und nochmals vielen Dank an diejenigen, die hier vor mir Antworten gepostet haben, ohne sie wäre ich nicht dazu gekommen ... also hier ist es:
quelle
const
?Wenn Sie die Lösung von @ David erweitern, können Sie eine rekursive Vorlage schreiben, die
integer_sequence
Semantikint N
um rekursive Iterationen zu zählenZ.B:
Wenn Ihr Funktor zur Kompilierungszeit nicht definiert ist (z. B. eine Nicht-
constexpr
Funktor-Instanz oder ein Lambda-Ausdruck), können Sie ihn alternativ als Funktionsparameter anstelle eines Klassenvorlagenparameters verwenden und die enthaltende Klasse tatsächlich vollständig entfernen:Für Callables mit Zeiger-zu-Mitglied-Funktion können Sie eines der oben genannten Codeteile ähnlich wie in der Antwort von @ David anpassen.
Erläuterung
In Bezug auf den zweiten Code gibt es zwei Vorlagenfunktionen: Die erste enthält den Funktor
func
, das Tupelt
mit TypenT...
und ein Parameterpaketargs
mit TypenArgs_tmp...
. Beim Aufruf werden die Objekte vont
Anfang (0
) bis Ende rekursiv nacheinander zum Parameterpaket hinzugefügt und die Funktion mit dem neuen inkrementierten Parameterpaket erneut aufgerufen.Die Signatur der zweiten Funktion ist fast identisch mit der ersten, außer dass der Typ
T...
für das Parameterpaket verwendet wirdargs
. Sobaldargs
die erste Funktion vollständig mit den Werten von gefüllt ist, lautett
ihr TypT...
(im Pseudocodetypeid(T...) == typeid(Args_tmp...)
), und der Compiler ruft stattdessen die zweite überladene Funktion auf, die wiederum aufruftfunc(args...)
.Der Code im Beispiel für den statischen Funktor funktioniert identisch, wobei der Funktor stattdessen als Klassenvorlagenargument verwendet wird.
quelle
Warum packen Sie Ihre variadischen Argumente nicht einfach in eine Tupelklasse und verwenden dann die Rekursion zur Kompilierungszeit (siehe Link ), um den Index abzurufen, an dem Sie interessiert sind. Ich finde, dass das Entpacken variadischer Vorlagen in einen Container oder eine Sammlung möglicherweise nicht typsicher für heterogene Typen ist
quelle
Args...
->tuple
, sonderntuple
->Args...
.Diese einfache Lösung funktioniert für mich:
quelle