Was sind C++-Funktoren und ihre Verwendung?

Was sind C++-Funktoren und ihre Verwendung?

Ein Funktor ist so ziemlich nur eine Klasse, die den Operator () definiert. Damit können Sie Objekte erstellen, die wie eine Funktion aussehen:

// this is a functor
struct add_x {
  add_x(int val) : x(val) {}  // Constructor
  int operator()(int y) const { return x + y; }

private:
  int x;
};

// Now you can use it like this:
add_x add42(42); // create an instance of the functor class
int i = add42(8); // and "call" it
assert(i == 50); // and it added 42 to its argument

std::vector<int> in; // assume this contains a bunch of values)
std::vector<int> out(in.size());
// Pass a functor to std::transform, which calls the functor on every element 
// in the input sequence, and stores the result to the output sequence
std::transform(in.begin(), in.end(), out.begin(), add_x(1)); 
assert(out[i] == in[i] + 1); // for all i

Es gibt ein paar nette Dinge über Funktoren. Einer ist, dass sie im Gegensatz zu regulären Funktionen Zustände enthalten können. Das obige Beispiel erstellt eine Funktion, die 42 zu dem hinzufügt, was Sie ihr geben. Aber dieser Wert 42 ist nicht fest codiert, er wurde als Konstruktorargument angegeben, als wir unsere Funktorinstanz erstellt haben. Ich könnte einen weiteren Addierer erstellen, der 27 addiert, indem ich einfach den Konstruktor mit einem anderen Wert aufrufe. Dadurch sind sie gut anpassbar.

Wie die letzten Zeilen zeigen, übergeben Sie häufig Funktoren als Argumente an andere Funktionen wie std::transform oder die anderen Standardbibliothekalgorithmen. Sie könnten dasselbe mit einem normalen Funktionszeiger machen, außer, wie ich oben sagte, können Funktoren "angepasst" werden, weil sie einen Zustand enthalten, was sie flexibler macht (Wenn ich einen Funktionszeiger verwenden wollte, müsste ich eine Funktion schreiben was zu seinem Argument genau 1 hinzufügt.Der Funktor ist allgemein und fügt alles hinzu, womit Sie ihn initialisiert haben), und sie sind möglicherweise auch effizienter. Im obigen Beispiel weiß der Compiler genau, welche Funktion std::transform ist sollte anrufen. Es sollte add_x::operator() aufrufen . Das bedeutet, dass dieser Funktionsaufruf inline ausgeführt werden kann. Und das macht es genauso effizient, als hätte ich die Funktion für jeden Wert des Vektors manuell aufgerufen.

Wenn ich stattdessen einen Funktionszeiger übergeben hätte, könnte der Compiler nicht sofort sehen, auf welche Funktion er zeigt, also müsste er den Zeiger zur Laufzeit dereferenzieren und dann den Aufruf machen, es sei denn, er führt einige ziemlich komplexe globale Optimierungen durch.


Kleine Ergänzung. Sie können boost::function verwenden , um Funktoren aus Funktionen und Methoden zu erstellen, wie folgt:

class Foo
{
public:
    void operator () (int i) { printf("Foo %d", i); }
};
void Bar(int i) { printf("Bar %d", i); }
Foo foo;
boost::function<void (int)> f(foo);//wrap functor
f(1);//prints "Foo 1"
boost::function<void (int)> b(&Bar);//wrap normal function
b(1);//prints "Bar 1"

und Sie können boost::bind verwenden, um diesem Funktor einen Zustand hinzuzufügen

boost::function<void ()> f1 = boost::bind(foo, 2);
f1();//no more argument, function argument stored in f1
//and this print "Foo 2" (:
//and normal function
boost::function<void ()> b1 = boost::bind(&Bar, 2);
b1();// print "Bar 2"

und am nützlichsten, mit boost::bind und boost::function können Sie einen Funktor aus einer Klassenmethode erstellen, eigentlich ist dies ein Delegate:

class SomeClass
{
    std::string state_;
public:
    SomeClass(const char* s) : state_(s) {}

    void method( std::string param )
    {
        std::cout << state_ << param << std::endl;
    }
};
SomeClass *inst = new SomeClass("Hi, i am ");
boost::function< void (std::string) > callback;
callback = boost::bind(&SomeClass::method, inst, _1);//create delegate
//_1 is a placeholder it holds plase for parameter
callback("useless");//prints "Hi, i am useless"

Sie können eine Liste oder einen Vektor von Funktoren erstellen

std::list< boost::function<void (EventArg e)> > events;
//add some events
....
//call them
std::for_each(
        events.begin(), events.end(), 
        boost::bind( boost::apply<void>(), _1, e));

Es gibt ein Problem mit all diesem Zeug, Compiler-Fehlermeldungen sind nicht lesbar :)


Ein Funktor ist ein Objekt, das sich wie eine Funktion verhält. Im Grunde eine Klasse, die operator() definiert .

class MyFunctor
{
   public:
     int operator()(int x) { return x * 2;}
}

MyFunctor doubler;
int x = doubler(5);

Der wirkliche Vorteil ist, dass ein Funktor den Zustand halten kann.

class Matcher
{
   int target;
   public:
     Matcher(int m) : target(m) {}
     bool operator()(int x) { return x == target;}
}

Matcher Is5(5);

if (Is5(n))    // same as if (n == 5)
{ ....}