Come verificare correttamente se std :: function è vuoto in C ++ 11?


96

Mi chiedevo come controllare correttamente se un std::functionè vuoto. Considera questo esempio:

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

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

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

Questo codice si compila bene in MSVC ma se chiamo doSomething()senza inizializzare eventFuncil codice ovviamente si blocca. È previsto, ma mi chiedevo qual è il valore di eventFunc? Il debugger dice 'empty'. Quindi l'ho risolto usando l'istruzione if semplice:

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

Funziona, ma mi chiedo ancora qual è il valore di non inizializzato std::function? Vorrei scrivere if (eventFunc != nullptr)ma std::function(ovviamente) non è un puntatore.

Perché il puro if funziona? Qual è la magia dietro? Ed è il modo corretto di controllarlo?


8
Nota che eventFuncnon è un lambda; è un std::function. Puoi memorizzare lambda in std::functions, ma non sono la stessa cosa.
templatetypedef

3
Hai ragione, ho cambiato il titolo per evitare confusione. Grazie.
NightElfik

Risposte:


104

Non stai verificando un lambda vuoto, ma se std::functionha un target richiamabile memorizzato in esso. Il controllo è ben definito e funziona per std::function::operator boolquesto consente la conversione implicita boolin contesti in cui sono richiesti valori booleani (come l'espressione condizionale in ifun'istruzione).

Inoltre, la nozione di lambda vuota non ha davvero senso. Dietro le quinte il compilatore converte un'espressione lambda in una definizione struct(o class), con le variabili acquisite memorizzate come membri dati di questa struct. Viene anche definito un operatore di chiamata di funzione pubblica, che è ciò che consente di invocare lambda. Quindi cosa sarebbe un lambda vuoto?


Puoi anche scrivere if(eventFunc != nullptr)se lo desideri, è equivalente al codice che hai nella domanda. std::function definisce operator== e operator!=sovraccarica per il confronto con un file nullptr_t.


1
Ma non == nullptrfa la stessa cosa? Sembra che ci sia un sovraccarico per l' ==operatore che causa un "vuoto" std::functionda confrontare truecon nullptr: cplusplus.com/reference/functional/function/operators
Kyle Strand

3
@KyleStrand Sì, anche il confronto con nullptrfunzionerà if(eventFunc != nullptr)è equivalente a if(eventFunc)nella domanda sopra.
Praetorian

3
Tecnicamente, std::function::operator boolnon consente la conversione implicita in bool. Dopotutto, è contrassegnato explicit, ma lo standard fa un'eccezione per alcuni costrutti di linguaggio che si aspettano espressioni booleane, definendolo "convertito contestualmente in bool". Puoi trovare lo snippet pertinente di standardese e una spiegazione qui: chris-sharpe.blogspot.com/2013/07/…
bcrist

@bcrist Sì, sono consapevole che l'operatore di conversione booleano è explicit, ecco perché sono stato attento a dichiarare che consente la conversione implicita boolin contesti in cui sono richiesti valori booleani . Questo è esattamente ciò che sta accadendo nel codice in questione.
Praetorian

5
@Praetorian Il punto che sto cercando di sottolineare è che lo standard assegna un significato molto specifico alla frase "conversione implicita", ed è nettamente diverso da "conversione contestuale in bool", che ha anche un significato molto specifico. Non esiste una relazione "È-a" qui. Capisco che i principianti probabilmente non hanno bisogno di conoscere subito la differenza tra conversione implicita / esplicita / contestuale, ma è meglio imparare le parole giuste inconsciamente, piuttosto che dover rompere le vecchie abitudini in seguito.
bcrist

21

Controlla qui http://www.cplusplus.com/reference/functional/function/operator_bool/

Esempio

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

Produzione

foo non è richiamabile.

il bar è richiamabile.


31
Penso che questa risposta sarebbe più chiara senza il swap(). Pensavo che l'output fosse al contrario finché non me ne sono reso conto.
cp.engr

-1

(Lasciatemi fornire una risposta chiara.)

Puoi controllare se un std::functionè vuoto con std::function::operator bool.

true: se l'oggetto è richiamabile.
false: altrimenti (l'oggetto è una funzione vuota)

Esempio

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

Produzione

foo non è vuoto.
il bar è vuoto.


2
Le stringhe dei risultati vengono scambiate.
Sophit

@Sophit Sei sicuro? ;)
zwcloud

1
Il tuo commento dice che foo non è vuoto e l'output non è d'accordo. Sono d'accordo con il tuo commento.
Sophit
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.