2013-02-18 10 views
57

Sie wissen, können wir eine Lambda-Funktion wickeln oder speichern std::function:Wie std :: Funktion arbeitet

#include <iostream> 
#include <functional> 
int main() 
{ 
    std::function<float (float, float)> add = [](float a, float b) 
    //   ^^^^^^^^^^^^^^^^^^^^ 
    { 
     return a + b; 
    }; 

    std::cout << add(1, 2) << std::endl; 
} 

Meine Frage ist, um std::function, wie Sie es ist eine Template-Klasse sehen, aber es kann annehmen jede Art von Funktion Signatur.

Zum Beispiel float (float, float) in dieser Form return_value (first_arg, second_arg).

Was ist die Struktur von std::function und wie akzeptiert es eine Funktion Signatur wie x(y,z) und wie es damit funktioniert? Ist float (float, float) ein neuer gültiger Ausdruck in C++?

+7

Nachschlagen Typ löschen in C++. –

+5

Sie können immer den '' Header Ihres Compilers öffnen (ich glaube, dass alle großen Compiler Standard-Header als C++ - Code versenden) und überprüfen, oder sehen Sie sich [Boost.Function] (http://www.boost.org/doc /libs/1_53_0/doc/html/function.html). – Angew

+4

@Angew: Ja, sehr lehrreich, +1. Ich denke, 'std :: function' berührt fast jeden Aspekt der C++ Sprache ... –

Antwort

93

Es verwendet einige type erasure technique.

Eine Möglichkeit besteht darin, den Mix-Subtyp-Polymorphismus mit Templates zu verwenden. Hier ist eine vereinfachte Version, nur ein Gefühl für die Gesamtstruktur zu geben:

template <typename T> 
struct function; 

template <typename Result, typename... Args> 
struct function<Result(Args...)> { 
private: 
    // this is the bit that will erase the actual type 
    struct concept { 
     virtual Result operator()(Args...) const = 0; 
    }; 

    // this template provides us derived classes from `concept` 
    // that can store and invoke op() for any type 
    template <typename T> 
    struct model : concept { 
     template <typename U> 
     model(U&& u) : t(std::forward<U>(u)) {} 

     Result operator()(Args... a) const override { 
      t(std::forward<Args>(a)...); 
     } 

     T t; 
    }; 

    // this is the actual storage 
    // note how the `model<?>` type is not used here  
    std::unique_ptr<concept> fn; 

public: 
    // construct a `model<T>`, but store it as a pointer to `concept` 
    // this is where the erasure "happens" 
    template <typename T, 
     typename=typename std::enable_if< 
      std::is_convertible< 
       decltype(t(std::declval<Args>()...)), 
       Result 
      >::value 
     >::type> 
    function(T&& t) 
    : fn(new model<typename std::decay<T>::type>(std::forward<T>(t))) {} 

    // do the virtual call  
    Result operator()(Args... args) const { 
     return (*fn)(std::forward<Args>(args)...); 
    } 
}; 

(Beachten Sie, dass ich einige Dinge, für die der Einfachheit halber übersehen: es kann nicht kopiert werden, und vielleicht andere Probleme, das nicht verwenden Code in echtem Code)

+6

+1. Es ist sehr bedauerlich, dass ich diese Antwort nicht öfter wiederholen kann. – xmllmx

+0

@Martinho, wo ist die Definition von 'Unqualifiziert'? – xmllmx

+2

@xmllmx Dies ist eine Aliasvorlage, die alle Qualifizierer (const, volatile, &, &&) von einem Typ entfernt. Selben wie 'Bare' hier: http://flamingdangerzone.com/cxx11/2012/05/29/type-traits-galore.html#bare_types (Ich habe mich seitdem geändert und festgestellt, dass Unqualified ein besserer Name ist :) –

Verwandte Themen