std::function
Die neuen Möglichkeiten für anonyme Funktionen, die C++ bietet, sind ja schön
und gut, aber: wie setze ich die eigentlich in eigenem Code ein? Der konkrete
Typ, der aus einem lambda-Ausdruck resultiert, ist ja so nicht bekannt. Zum
Glück gibt es dafür std::function: diese Template-Klasse realisiert ein
Funktionsobjekt, was sich genau auf den gewünschten Funktionstyp parametrisieren
lässt und dann so einiges an Flexibilität bietet.
Code
Der im Video verwendete Code enthält leider einen Fehler (unten im Code-Beispiel korrigiert). Daher noch zusätzlich ein Korrekturvideo.
Code
#include <iostream>
#include <functional>
void repeat_n(unsigned int n, std::function<void ()> const &f)
{
for (unsigned int i = 0; i < n; ++i) {
f();
}
}
void f_ptr()
{
std::cerr << "Legacy-Funktion\n";
}
struct FObj
{
void operator()()
{
std::cerr << "Funktionsobjekt\n";
}
};
int main()
{
repeat_n(3, []() {
std::cerr << "Lambda-Funktion\n";
});
repeat_n(3, &f_ptr);
repeat_n(3, FObj());
}
Erklärung
Ziel ist es hier, einen eigenen Algorithmus nach dem Vorbild der Algorithmen der
Standardbibliothek zu implementieren. Die Funktion repeat_n soll eine ihr
übergebene Funktion n mal ausführen. Das ganze soll natürlich mit
Lambda-Funktionen arbeiten, die sich für sowas ja geradezu anbieten. Die
Standardbibliothek stellt dafür std::function bereit. Diese Template-Klasse
wird mit der Signatur der erwarteten Funktion parametrisiert und implementiert
dann gleich einen passenden operator(). Man kann dieses Objekt dann also
einfach wie die betreffende Funktion aufrufen. Das ist allerdings nur der erste
Teil. Zur brauchbaren Verwendung müssen nun noch ein paar Konstruktoren her, die
eine automatische Konvertierung aus den verschiedenen Zielfunktionen erlauben:
einer, der Lambda-Funktionen umwandelt, einer für Funktionsobjekte und einer für
Funktionspointer. Alle drei Möglichkeiten bietet std::function und erlaubt so,
verschiedene Möglichkeiten, eine Funktion als Parameter zu übergeben, mit einem
einheitlichen Interface zu behandeln. repeat_n macht davon Gebrauch, wie in
den Zeilen 26, 29 und 30 zu sehen ist. Die erste Variante nimmt eine
Lambda-Funktion, die zweite einen Funktionspointer und die dritte ein
Funktionsobjekt. Alle drei funktionieren letzten Endes gleich: der zweite
Parameter wird n mal aufgerufen.