C++11 leitet den Typ nicht ab, wenn std::function- oder Lambda-Funktionen beteiligt sind

Lesezeit: 8 Minuten

C11 leitet den Typ nicht ab wenn stdfunction oder Lambda Funktionen
Datenkunde

Wenn ich diese Funktion definiere,

template<class A>
set<A> test(const set<A>& input) {
    return input;
}

Ich kann es mit aufrufen test(mySet) an anderer Stelle im Code, ohne den Vorlagentyp explizit definieren zu müssen. Wenn ich jedoch die folgende Funktion verwende:

template<class A>
set<A> filter(const set<A>& input,function<bool(A)> compare) {
    set<A> ret;
    for(auto it = input.begin(); it != input.end(); it++) {
        if(compare(*it)) {
            ret.insert(*it);
        }
    }
    return ret;
}

Wenn ich diese Funktion mit aufrufe filter(mySet,[](int i) { return i%2==0; });
Ich bekomme folgenden Fehler:

Fehler: keine passende Funktion für den Aufruf von ‘filter(std::set&, main()::)’

Allerdings alle diese Versionen tun Arbeit:

std::function<bool(int)> func = [](int i) { return i%2 ==0; };
set<int> myNewSet = filter(mySet,func);

set<int> myNewSet = filter<int>(mySet,[](int i) { return i%2==0; });

set<int> myNewSet = filter(mySet,function<bool(int)>([](int i){return i%2==0;}));

Warum kann c++11 den Vorlagentyp nicht erraten, wenn ich die Lambda-Funktion direkt in den Ausdruck einfüge, ohne direkt eine zu erstellen std::function?

BEARBEITEN:

Auf Anraten von Luc Danton in den Kommentaren gibt es hier eine Alternative zu der Funktion, die ich zuvor hatte und bei der die Vorlagen nicht explizit übergeben werden müssen.

template<class A,class CompareFunction>
set<A> filter(const set<A>& input,CompareFunction compare) {
    set<A> ret;
    for(auto it = input.begin(); it != input.end(); it++) {
        if(compare(*it)) {
            ret.insert(*it);
        }
    }
    return ret;
}

Diese kann per angerufen werden set<int> result = filter(myIntSet,[](int i) { i % 2 == 0; }); ohne die Vorlage zu benötigen.

Der Compiler kann sogar bis zu einem gewissen Grad die Rückgabetypen erraten, indem er das neue Schlüsselwort decltype und die Syntax des neuen Funktionsrückgabetyps verwendet. Hier ist ein Beispiel, das einen Satz in eine Karte umwandelt, wobei eine Filterfunktion und eine Funktion verwendet werden, die die Schlüssel basierend auf den Werten generiert:

template<class Value,class CompareType,class IndexType>
auto filter(const set<Value>& input,CompareType compare,IndexType index) -> map<decltype(index(*(input.begin()))),Value> {
    map<decltype(index(*(input.begin()))),Value> ret;
    for(auto it = input.begin(); it != input.end(); it++) {
        if(compare(*it)) {
            ret[index(*it)] = *it;
        }
    }
    return ret;
}

Es kann auch aufgerufen werden, ohne das Template direkt zu verwenden, as

map<string,int> s = filter(myIntSet,[](int i) { return i%2==0; },[](int i) { return toString(i); });

  • Unabhängig von Ihrer Frage, aber Sie wissen, dass Ihre filter ist im Wesentlichen äquivalent zu einer nicht generischen Version von std::copy_ifnicht wahr?

    – Jerry Sarg

    3. April 2012 um 17:45 Uhr


  • Ah, std::copy_if war mir nicht bekannt, danke für den Hinweis. Dies ist jedoch Teil einer größeren Gruppe von 4 Funktionen, eine, die set => map beim Filtern konvertiert, und ich sehe keine Möglichkeit, dies mit copy_if zu implementieren und dem Benutzer zu ermöglichen, Schlüssel mit den Werten im Set zu erstellen. Aus Gründen der Konsistenz in der Verwendung entscheide ich mich dafür, dies auf diese Weise zu tun.

    – Datenkunde

    3. April 2012 um 18:27 Uhr

  • Für die Aufzeichnung, wenn Sie einen Funktor akzeptieren möchten, ist es normalerweise idiomatisch, ihn zu einem allgemeinen Vorlagenparameter zu machen, dh template<typename A, typename Predicate> set<A> filter(set<A> const& input, Predicate compare);. Wie Sie gerade gesehen haben, std::function funktioniert nicht, um zu dokumentieren, dass das übergebene Prädikat eine Signaturübereinstimmung haben sollte bool(A); es gibt andere Möglichkeiten, das zu tun. Weiterhin gibt es andere Nachteile bei der Verwendung std::function als Funktionsargument.

    – Luc Danton

    4. April 2012 um 3:11 Uhr

C11 leitet den Typ nicht ab wenn stdfunction oder Lambda Funktionen
David Rodríguez – Dribeas

Das Problem liegt in der Natur von Lambdas. Sie sind Funktionsobjekte mit einem festen Satz von Eigenschaften gemäß dem Standard, aber sie sind es nicht eine Funktion. Der Standard legt fest, dass Lambdas umgewandelt werden können std::function<> mit den genauen Arten von Argumenten und, falls sie keinen Zustand haben, Funktionszeigern.

Aber das bedeutet nicht, dass ein Lambda ein ist std::function noch ein Funktionszeiger. Sie sind einzigartige Arten der Umsetzung operator().

Die Typableitung hingegen wird nur exakte Typen ableiten, ohne Konvertierungen (außer konstanten/flüchtigen Qualifikationen). Denn das Lambda ist nicht a std::function der Compiler kann den Typ im Aufruf nicht ableiten: filter(mySet,[](int i) { return i%2==0; }); irgendeine zu sein std::function<> Instanziierung.

Wie bei den anderen Beispielen konvertieren Sie im ersten das Lambda in den Funktionstyp und übergeben diesen dann. Der Compiler kann dort den Typ ableiten, wie im dritten Beispiel, wo die std::function ist ein rvalue (temporär) desselben Typs.

Wenn Sie den instanziierenden Typ angeben int Für die Vorlage, zweites Arbeitsbeispiel, kommt der Abzug nicht ins Spiel, der Compiler verwendet den Typ und konvertiert dann das Lambda in den entsprechenden Typ.

  • Beachten Sie, dass es nicht das Lambda ist, das die Konvertierung durchführt std::functiones ist std::function die alles akzeptiert, was aufrufbar ist.

    – Xeo

    25. April 2012 um 4:38 Uhr

  • Die Typableitung führt auch Basistyp-“Konvertierungen” durch, um vollständig zu sein.

    – Yakk – Adam Nevraumont

    23. März 2015 um 14:13 Uhr

  • Es kann zum Laufen gebracht werden, indem der Abzug für die deaktiviert wird compare Arg. Wir können dies mit dieser Signatur tun: template<class A> set<A> filter(const set<A>& input ,typename NoOp<function<bool(A)> >::type compare ) wo Noop ist definiert als template <typename T> struct NoOp { typedef T type; };

    – Aaron McDaid

    29. Juni 2015 um 15:45 Uhr

  • … Dies erlaubt A vom ersten arg abzuleiten, dann unverändert für die zu verwenden compare Arg.

    – Aaron McDaid

    29. Juni 2015 um 15:46 Uhr

  • @AaronMcDaid Das ist eine nette Lösung, aber ich möchte keine einführen NoOp struct sehr Zeit, die ich brauche. Weißt du zufällig, ob eine solche NoOp Typ ist in modernen C++-Standards als Teil von STL verfügbar? Es scheint nützlich zu sein.

    – bluenote10

    19. November 2018 um 15:56 Uhr

C11 leitet den Typ nicht ab wenn stdfunction oder Lambda Funktionen
Nawaz

Vergiss deinen Fall. da dies zu komplex für eine Analyse ist.

Nehmen Sie dieses einfache Beispiel:

 template<typename T>
 struct X 
 {
     X(T data) {}
 };

 template<typename T>
 void f(X<T> x) {}

Ruf jetzt an f als:

 f(10); 

Hier könnte man versucht sein, das zu denken T wird davon abgeleitet int und Daher sollte der obige Funktionsaufruf funktionieren. Nun, das ist nicht der Fall. Um die Sache einfach zu halten, stellen Sie sich vor, dass es eine gibt Ein weiterer Konstruktor, der dauert int als:

 template<typename T>
 struct X 
 {
     X(T data) {}
     X(int data) {} //another constructor
 };

Was jetzt T sollte abgeleitet werden, wenn ich schreibe f(10)? Brunnen, T könnten irgendein Art.

Beachten Sie, dass es viele andere solche Fälle geben könnte. Nehmen Sie zum Beispiel diese Spezialisierung:

 template<typename T>
 struct X<T*>         //specialized for pointers
 {
    X(int data) {}; 
 };

Was jetzt T sollte für den Anruf abgeleitet werden f(10)? Jetzt scheint es noch schwieriger zu sein.

Es handelt sich daher um einen nicht ableitbaren Kontext, der erklärt, warum Ihr Code nicht funktioniert std::function Das ist ein identischer Fall – sieht nur an der Oberfläche komplex aus. Beachten Sie, dass Lambdas sind nicht typ std::function – Sie sind im Grunde genommen Instanzen von Compiler-generierten Klassen (d. h. sie sind Funktoren von unterschiedlich Typen als std::function).

Wenn wir haben:

template <typename R, typename T>
int myfunc(std::function<R(T)> lambda)
{
  return lambda(2);
}

int r = myfunc([](int i) { return i + 1; });

Es wird nicht kompiliert. Aber wenn Sie vorher erklären:

template <typename Func, typename Arg1>
static auto getFuncType(Func* func = nullptr, Arg1* arg1 = nullptr) -> decltype((*func)(*arg1));

template <typename Func>
int myfunc(Func lambda)
{
  return myfunc<int, decltype(getFuncType<Func, int>())>(lambda);
}

Sie können Ihre Funktion problemlos mit einem Lambda-Parameter aufrufen.

Hier gibt es 2 neue Codeteile.

Zuerst haben wir eine Funktionsdeklaration, die nur nützlich ist, um einen Funktionszeigertyp im alten Stil basierend auf gegebenen Vorlagenparametern zurückzugeben:

template <typename Func, typename Arg1>
static auto getFuncType(Func* func = nullptr, Arg1* arg1 = nullptr) -> decltype((*func)(*arg1)) {};

Zweitens haben wir eine Funktion, die ein Vorlagenargument verwendet, um unseren erwarteten Lambda-Typ zu erstellen, der „getFuncType“ aufruft:

template <typename Func>
int myfunc(Func lambda)
{
  return myfunc<int, decltype(getFuncType<Func, int>())>(lambda);
}

Mit den richtigen Template-Parametern können wir jetzt das echte „myfunc“ aufrufen. Vollständiger Code wird sein:

template <typename R, typename T>
int myfunc(std::function<R(T)> lambda)
{
  return lambda(2);
}

template <typename Func, typename Arg1>
static auto getFuncType(Func* func = nullptr, Arg1* arg1 = nullptr) -> decltype((*func)(*arg1)) {};

template <typename Func>
int myfunc(Func lambda)
{
  return myfunc<int, decltype(getFuncType<Func, int>())>(lambda);
}

int r = myfunc([](int i) { return i + 1; });

Sie können jede Überladung für „getFuncType“ so deklarieren, dass sie mit Ihrem Lambda-Parameter übereinstimmt. Zum Beispiel:

template <typename Func, typename Arg1, typename Arg2>
static auto getFuncType(Func* func = nullptr, Arg1* arg1 = nullptr, Arg2* arg2 = nullptr) -> decltype((*func)(*arg1, *arg2)) {};

  • Lassen Sie mich anmerken, dass ich dies gelernt habe aus: Boolinq-Projekt Verknüpfung

    – Marianop

    29. April 2019 um 18:56 Uhr

993130cookie-checkC++11 leitet den Typ nicht ab, wenn std::function- oder Lambda-Funktionen beteiligt sind

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy