Comment vérifier correctement si std :: function est vide en C ++ 11?

96

Je me demandais comment vérifier correctement si un std::functionest vide. Prenons cet exemple:

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

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

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

Ce code se compile très bien dans MSVC mais si j'appelle doSomething()sans initialiser eventFuncle code se bloque évidemment. C'est attendu mais je me demandais quelle est la valeur du eventFunc? Le débogueur dit 'empty'. J'ai donc corrigé cela en utilisant une instruction if simple:

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

Cela fonctionne mais je me demande toujours quelle est la valeur de non-initialized std::function? Je voudrais écrire if (eventFunc != nullptr)mais ce std::functionn'est (évidemment) pas un pointeur.

Pourquoi le pur si fonctionne? Quelle est la magie derrière ça? Et, est-ce la bonne façon de le vérifier?

NightElfik
la source
8
Notez que ce eventFuncn'est pas un lambda; c'est un std::function. Vous pouvez stocker des lambdas dans des std::functions, mais ce n'est pas la même chose.
templatetypedef
3
Vous avez raison, j'ai changé le titre pour éviter toute confusion. Merci.
NightElfik

Réponses:

104

Vous ne vérifiez pas un lambda vide, mais si le std::functioncontient une cible appelable. Le chèque est bien défini et fonctionne grâce àstd::function::operator bool elle permet une conversion implicite booldans des contextes où des valeurs booléennes sont requises (comme l'expression conditionnelle dans une ifinstruction).

De plus, la notion de lambda vide n'a pas vraiment de sens. Dans les coulisses, le compilateur convertit une expression lambda en une struct(ou class) définition, avec les variables que vous capturez stockées en tant que données membres de this struct. Un opérateur d'appel de fonction publique est également défini, ce qui vous permet d'appeler le lambda. Alors, que serait un lambda vide?


Vous pouvez également écrire if(eventFunc != nullptr)si vous le souhaitez, c'est équivalent au code que vous avez dans la question. std::function définit operator== et operator!=surcharge pour comparer avec un nullptr_t.

Prétorien
la source
1
== nullptrMais ne fait pas la même chose? Il semble qu'il y ait une surcharge pour l' ==opérateur qui provoque un "vide" std::functionà comparer trueavec nullptr: cplusplus.com/reference/functional/function/operators
Kyle Strand
3
@KyleStrand Oui, la comparaison avec nullptrfonctionnera aussi, if(eventFunc != nullptr)équivaut à if(eventFunc)la question ci-dessus.
Prétorien
3
Techniquement, std::function::operator booln'autorise pas la conversion implicite en bool. Il est marqué explicitaprès tout, mais le standard fait une exception pour certaines constructions de langage qui attendent des expressions booléennes, l'appelant "contextuellement converti en bool". Vous pouvez trouver l'extrait pertinent de standardese et une explication ici: chris-sharpe.blogspot.com/2013/07/…
bcrist
@bcrist Oui, je suis conscient que l'opérateur de conversion booléen est explicit, c'est pourquoi j'ai pris soin de préciser qu'il permet une conversion implicite booldans des contextes où des valeurs booléennes sont requises . C'est exactement ce qui se passe dans le code en question.
Prétorien
5
@Praetorian Le point que j'essaie de faire valoir est que la norme attribue une signification très spécifique à l'expression «conversion implicite», et elle est distinctement différente de «conversion contextuelle en booléen», qui a également une signification très spécifique. Il n'y a pas de relation «Est-un» ici. Je comprends que les débutants n'ont probablement pas besoin de connaître tout de suite la différence entre la conversion implicite / explicite / contextuelle, mais il vaut mieux apprendre les bons mots inconsciemment, que de devoir briser les vieilles habitudes plus tard.
bcrist
21

Vérifiez ici http://www.cplusplus.com/reference/functional/function/operator_bool/

Exemple

// 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;
}

Production

foo n'est pas appelable.

bar est appelable.

Dawid Drozd
la source
31
Je pense que cette réponse serait plus claire sans le swap(). Je pensais que la sortie était à l'envers jusqu'à ce que je m'en rende compte.
cp.engr
-1

(Permettez-moi de fournir une réponse claire.)

Vous pouvez vérifier si a std::functionest vide avec std::function::operator bool.

true: si l'objet est appelable.
false: sinon (l'objet est une fonction vide)

Exemple

#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;
}

Production

foo n'est pas vide.
la barre est vide.

zwcloud
la source
2
Vos chaînes de résultats sont permutées.
Sophit
@Sophit Êtes-vous sûr? ;)
zwcloud
1
Votre commentaire indique que foo n'est pas vide et que la sortie n'est pas d'accord. Je suis d'accord avec votre commentaire.
Sophit