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.

Video

Quellcode

#include <iostream>
#include <functional>

void repeat_n(unsigned int n, std::function<void ()> const &f)
{
    for (auto 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.