Wie kann ich richtig überprüfen, ob die Funktion std :: in C ++ 11 leer ist?

95

Ich habe mich gefragt, wie ich richtig prüfen soll, ob ein std::functionleer ist. Betrachten Sie dieses Beispiel:

class Test {
    std::function<void(int a)> eventFunc;

    void registerEvent(std::function<void(int a)> e) {
        eventFunc = e;
    }

    void doSomething() {
        ...
        eventFunc(42);
    }
};

Dieser Code wird in MSVC einwandfrei kompiliert, aber wenn ich doSomething()ohne Initialisierung aufrufe, eventFuncstürzt der Code offensichtlich ab. Das wird erwartet, aber ich habe mich gefragt, welchen Wert das hat eventFunc. Der Debugger sagt 'empty'. Also habe ich das mit einer einfachen if-Anweisung behoben:

   void doSomething() {
        ...
        if (eventFunc) {
            eventFunc(42);
        }
   }

Das funktioniert, aber ich frage mich immer noch, welchen Wert nicht initialisiert hat std::function. Ich würde gerne schreiben if (eventFunc != nullptr), std::functionist aber (offensichtlich) kein Zeiger.

Warum funktioniert das reine wenn? Was ist die Magie dahinter? Und ist es die richtige Art, es zu überprüfen?

NightElfik
quelle
8
Beachten Sie, dass dies eventFunckein Lambda ist. es ist ein std::function. Sie können Lambdas in std::functions speichern , aber sie sind nicht dasselbe.
Templatetypedef
3
Sie haben Recht, ich habe den Titel geändert, um Verwirrung zu vermeiden. Vielen Dank.
NightElfik

Antworten:

103

Sie suchen nicht nach einem leeren Lambda, sondern danach, ob in dem std::functionein aufrufbares Ziel gespeichert ist. Die Prüfung ist genau definiert und funktioniert, std::function::operator boolwodurch eine implizite Konvertierung boolin Kontexte ermöglicht wird, in denen boolesche Werte erforderlich sind (z. B. der bedingte Ausdruck in einer ifAnweisung).

Außerdem macht die Vorstellung eines leeren Lambda keinen Sinn. Hinter den Kulissen konvertiert der Compiler einen Lambda-Ausdruck in eine struct(oder class) Definition, wobei die von Ihnen erfassten Variablen als Datenelemente gespeichert werden struct. Es wird auch ein öffentlicher Funktionsaufrufoperator definiert, mit dem Sie das Lambda aufrufen können. Was wäre ein leeres Lambda?


Sie können auch schreiben, if(eventFunc != nullptr)wenn Sie möchten. Dies entspricht dem Code, den Sie in der Frage haben. std::function definiert operator== und operator!=überlastet zum Vergleich mit a nullptr_t.

Prätorianer
quelle
1
Macht das nicht == nullptrdasselbe? Es sieht so aus, als ob es eine Überlastung für den ==Bediener geben sollte, die dazu führt, dass ein "Leer" std::functionverglichen wird truemit nullptr: cplusplus.com/reference/functional/function/operators
Kyle Strand
3
@KyleStrand Ja, der Vergleich mit nullptrfunktioniert auch, if(eventFunc != nullptr)entspricht if(eventFunc)der obigen Frage.
Prätorianer
3
Technisch std::function::operator boolerlaubt keine implizite Konvertierung nach bool. Es ist zwar markiert explicit, aber der Standard macht eine Ausnahme für bestimmte Sprachkonstrukte, die boolesche Ausdrücke erwarten, und nennt es "kontextuell in bool konvertiert". Den relevanten Ausschnitt von Standardese und eine Erklärung finden Sie hier: chris-sharpe.blogspot.com/2013/07/…
bcrist
@bcrist Ja, ich bin mir bewusst, dass der boolesche Konvertierungsoperator ist explicit. Deshalb habe ich sorgfältig angegeben, dass eine implizite Konvertierung boolin Kontexten möglich ist, in denen boolesche Werte erforderlich sind . Genau das passiert in dem fraglichen Code.
Prätorianer
5
@Praetorian Der Punkt, den ich ansprechen möchte, ist, dass der Standard dem Ausdruck "implizite Konvertierung" eine sehr spezifische Bedeutung zuweist und sich deutlich von der "kontextuellen Konvertierung in Bool" unterscheidet, die auch eine sehr spezifische Bedeutung hat. Hier gibt es keine "Is-a" -Beziehung. Ich verstehe, dass Anfänger den Unterschied zwischen impliziter / expliziter / kontextueller Konvertierung wahrscheinlich nicht sofort kennen müssen, aber es ist besser, die richtigen Wörter unbewusst zu lernen, als später alte Gewohnheiten zu brechen.
bcrist
21

Überprüfen Sie hier http://www.cplusplus.com/reference/functional/function/operator_bool/

Beispiel

// function::operator bool example
#include <iostream>     // std::cout
#include <functional>   // std::function, std::plus

int main () {
  std::function<int(int,int)> foo,bar;
  foo = std::plus<int>();

  foo.swap(bar);

  std::cout << "foo is " << (foo ? "callable" : "not callable") << ".\n";
  std::cout << "bar is " << (bar ? "callable" : "not callable") << ".\n";

  return 0;
}

Ausgabe

foo ist nicht aufrufbar.

Bar ist aufrufbar.

Dawid Drozd
quelle
31
Ich denke, diese Antwort wäre ohne die klarer swap(). Ich dachte, die Ausgabe sei rückwärts, bis ich es merkte.
cp.engr
-1

(Lassen Sie mich eine klare Antwort geben.)

Sie können überprüfen, ob a std::functionmit leer ist std::function::operator bool.

true: Wenn das Objekt aufrufbar ist.
false: andernfalls (das Objekt ist eine leere Funktion)

Beispiel

#include <iostream>
#include <functional>

int main ()
{
    std::function<int(int,int)> foo = std::plus<int>();//assigned: not empty
    std::function<int(int,int)> bar;//not assigned: empty

    std::cout << "foo is " << (foo ? "not empty" : "empty") << ".\n";
    std::cout << "bar is " << (bar ? "not empty" : "empty") << ".\n";

    return 0;
}

Ausgabe

foo ist nicht leer.
Bar ist leer.

zwcloud
quelle
2
Ihre Ergebniszeichenfolgen werden ausgetauscht.
Sophit
@Sophit Bist du sicher? ;)
zwcloud
1
Ihr Kommentar besagt, dass foo nicht leer ist und die Ausgabe nicht übereinstimmt. Ich stimme Ihrem Kommentar zu.
Sophit