Ist es möglich, ein Parameterpaket für eine spätere Verwendung zu speichern?
template <typename... T>
class Action {
private:
std::function<void(T...)> f;
T... args; // <--- something like this
public:
Action(std::function<void(T...)> f, T... args) : f(f), args(args) {}
void act(){
f(args); // <--- such that this will be possible
}
}
Dann später:
void main(){
Action<int,int> add([](int x, int y){std::cout << (x+y);}, 3, 4);
//...
add.act();
}
c++
c++11
variadic-templates
Eric B.
quelle
quelle
Antworten:
Um das zu erreichen, was Sie hier tun möchten, müssen Sie Ihre Vorlagenargumente in einem Tupel speichern:
Außerdem müssen Sie Ihren Konstruktor ein wenig ändern. Insbesondere das Initialisieren
args
mit einemstd::make_tuple
und das Zulassen universeller Referenzen in Ihrer Parameterliste:Außerdem müssten Sie einen Sequenzgenerator wie folgt einrichten:
Und Sie können Ihre Methode so implementieren, dass Sie einen solchen Generator verwenden:
Und das ist es! Jetzt sollte Ihre Klasse so aussehen:
Hier ist dein vollständiges Programm auf Coliru.
Update: Hier ist eine Hilfsmethode, mit der die Angabe der Vorlagenargumente nicht erforderlich ist:
Und wieder hier ist eine weitere Demo.
quelle
void print(const std::string&); std::string hello(); auto act = make_action(print, hello());
ist nicht gut. Ich würde das Verhalten von bevorzugenstd::bind
, das eine Kopie jedes Arguments erstellt, es sei denn, Sie deaktivieren dies mitstd::ref
oderstd::cref
.args(std::make_tuple(std::forward<Args>(args)...))
zuargs(std::forward<Args>(args)...)
. Übrigens habe ich das vor langer Zeit geschrieben und ich würde diesen Code nicht verwenden, um eine Funktion an einige Argumente zu binden. Ich würde nur verwendenstd::invoke()
oderstd::apply()
heutzutage.Sie können
std::bind(f,args...)
dies verwenden. Es wird ein bewegliches und möglicherweise kopierbares Objekt generiert, in dem eine Kopie des Funktionsobjekts und jedes der Argumente zur späteren Verwendung gespeichert wird:Beachten Sie, dass dies
std::bind
eine Funktion ist und Sie das Ergebnis des Aufrufs als Datenelement speichern müssen. Der Datentyp dieses Ergebnisses ist nicht leicht vorherzusagen (der Standard spezifiziert ihn nicht einmal genau), daher verwende ich eine Kombination ausdecltype
undstd::declval
, um diesen Datentyp zur Kompilierungszeit zu berechnen. Siehe die Definition vonAction::bind_type
oben.Beachten Sie auch, wie ich universelle Referenzen im Vorlagenkonstruktor verwendet habe. Dies stellt sicher, dass Sie Argumente übergeben können, die nicht
T...
genau mit den Klassenvorlagenparametern übereinstimmen (z. B. können Sie rWert-Verweise auf einige der Parameter verwenden und diese unverändertT
an denbind
Aufruf weiterleiten .)Schlussbemerkung: Wenn Sie Argumente als Referenzen speichern möchten (damit die von Ihnen übergebene Funktion sie ändern und nicht nur verwenden kann), müssen Sie
std::ref
sie in Referenzobjekte einschließen. Durch einfaches Übergeben von aT &
wird eine Kopie des Werts erstellt, keine Referenz.Betriebscode auf Coliru
quelle
add
sie in einem anderen Bereich definiert sind als woact()
? Sollte der Konstruktor nichtConstrT&... args
eher bekommen alsConstrT&&... args
?bind()
? Dabind()
garantiert Kopien erstellt werden (oder in frisch erstellte Objekte verschoben werden), kann es meines Erachtens kein Problem geben.bind_(f, std::forward<ConstrT>(args)...)
ist das Verhalten gemäß dem Standard undefiniert, da dieser Konstruktor implementierungsdefiniert ist.bind_type
wird als kopierbar und / oder verschiebbar konstruierbar angegeben,bind_{std::bind(f, std::forward<ConstrT>(args)...)}
sollte also weiterhin funktionieren.Ich denke du hast ein XY Problem. Warum sollten Sie sich die Mühe machen, das Parameterpaket zu speichern, wenn Sie nur ein Lambda auf der Call-Site verwenden könnten? dh
quelle
Diese Frage war aus C ++ 11 Tage. Aber für diejenigen, die es jetzt in den Suchergebnissen finden, einige Updates:
Ein
std::tuple
Mitglied ist immer noch die einfache Möglichkeit, Argumente allgemein zu speichern. (Einestd::bind
Lösung ähnlich der von @ jogojapan funktioniert auch, wenn Sie nur eine bestimmte Funktion aufrufen möchten, aber nicht, wenn Sie auf andere Weise auf die Argumente zugreifen oder die Argumente an mehr als eine Funktion übergeben möchten usw.)In C ++ 14 und höher
std::make_index_sequence<N>
oderstd::index_sequence_for<Pack...>
kann dashelper::gen_seq<N>
in der Lösung von 0x499602D2 gezeigte Tool ersetzen :In C ++ 17 und höher
std::apply
kann das Auspacken des Tupels verwendet werden:Hier ist ein vollständiges C ++ 17-Programm, das die vereinfachte Implementierung zeigt. Ich habe auch aktualisiert
make_action
, um Referenztypen in der zu vermeidentuple
, was für rvalue-Argumente immer schlecht und für lvalue-Argumente ziemlich riskant war.quelle