2010-02-08 12 views

Antwort

11

Der letzte Parameter von for_each Vorlage ist ein Funktor. Functor ist etwas, das mit dem Operator () (möglicherweise mit Argumenten) "aufgerufen" werden kann. Per Definition gibt es zwei verschiedene Arten von Funktoren:

  1. Gewöhnliche Nichtmitglied Funktionen sind Funktoren.
  2. Objekte der Klassenart mit überladen () Operator (so genannte Funktionsobjekte) sind auch Funktoren.

Nun, wenn Sie eine normale Funktion als Funktor für for_each verwenden wollte, wäre es so etwas wie die folgenden

inline void do_something(int &i) { /* do something */ } 

int main() { 
    int array[10]; 
    std::for_each(array, array + 10, &do_something); 
} 

In diesem Fall sehen die for_each Vorlage mit instanziiert [abgeleitet] Argumente <int *, void (*)(int &)> . Beachten Sie, dass der tatsächliche Funktorwert in diesem Fall der Funktionszeiger &do_something ist, der als Funktionsargument übergeben wurde. Aus Sicht von for_each Funktion ist dies ein Laufzeitwert. Da es sich um einen Laufzeitwert handelt, können die Aufrufe an den Funktor nicht inline ausgeführt werden. (Genauso wie es in der Regel unmöglich ist, jeden durch einen Funktionszeiger ausgeführten Aufruf zu inline zu schreiben).

Aber wenn wir eine Funktion Objekt stattdessen verwenden, könnte der Code aussehen, als

struct do_something { 
    void operator()(int &i) { /* do something */ } 
}; 

int main() { 
    int array[10]; 
    std::for_each(array, array + 10, do_something()); 
} 

In diesem Fall folgt die for_each Vorlage mit [abgeleitet] Argumente instanziiert wird <int *, do_something>. Die Aufrufe an den Funktor von innen for_each werden an do_something::operator() geleitet. Das Ziel für den Aufruf ist bekannt und wird zur Kompilierungszeit festgelegt. Da die Zielfunktion zur Kompilierungszeit bekannt ist, kann der Aufruf leicht inline durchgeführt werden.Im letzten Fall wird natürlich auch ein Laufzeitwert als Argument an for_each übergeben. Es ist eine [möglicherweise "Dummy" temporäre] Instanz von do_something Klasse, die wir erstellen, wenn wir for_each aufrufen. Dieser Laufzeitwert hat jedoch keine Auswirkung auf das Ziel für den Aufruf (es sei denn, die operator() ist virtuell), sodass das Inlining nicht betroffen ist.

+0

Aber die vollständige Definition von ':: std :: for_each' steht dem Compiler zur Verfügung. Für einen intelligenten Compiler sollte er sehen, dass er mit einem bestimmten Argument aufgerufen wird, das die Adresse einer inline deklarierten Funktion ist. Der Compiler sollte in der Lage sein, den Funktionsaufruf genauso gut zu kapseln, als wäre es eine Klassenmethode. – Omnifarious

+2

@Onmifarious: In meinem Beispiel können Sie 'for_each' mit verschiedenen Funktionen aufrufen, die' void (int &) '-Signatur haben und der Compiler wird gezwungen, dieselbe, eine und einzige Instanz von' for_each' zu verwenden: 'for_each AnT

+2

Zum Beispiel (vergessen Sie Vorlagen), wenn ich die Funktion 'void foo (int)' in meinem Programm habe und sie mehrmals als 'foo (1); foo (2); foo (3); 'Im Allgemeinen erwarte ich, dass der Compiler einen Funktionskörper für' foo' generiert und denselben Body mit verschiedenen Argumenten ausführt: 1, 2 und 3. Wenn ich feststelle, dass der Compiler stattdessen 3 verschiedene Körper von 'foo erzeugt hat Mit "inline" -Konstanten 1, 2 und 3 werde ich unangenehm überrascht sein. Für kleine 'foo' wäre das OK (wenn es danach inline ist), aber für ein größeres' foo' ist das unerwünscht. Was du beschreibst, ist im Wesentlichen dasselbe. – AnT

2

Inlining bedeutet nur, jeden Aufruf dieser Funktion durch den Rumpf dieser Funktion zu ersetzen.

Es ist eine Optimierung für kleine Funktionen, weil es den Overhead des Springens auf eine neue Funktion reduziert und dann zurückkehrt.

3

Dies bedeutet, dass die Definition der Funktion (Code) kopiert werden kann und Sie vor einem Funktionsaufruf (der auf manchen Systemen als teuer gilt) gespeichert werden. Denken Sie an Makroersatz.

4

Inline ist der Prozess, bei dem ein Compiler einen Aufruf einer Funktion durch den Inhalt der Funktion ersetzen kann. Der Compiler muss den Inhalt der Funktion kennen, wenn er kompiliert wird.

Der Compiler kann dies oft nicht tun, wenn ein Funktionszeiger übergeben wird.