Jak poprawnie sprawdzić, czy funkcja std :: jest pusta w C ++ 11?

99

Zastanawiałem się, jak prawidłowo sprawdzić, czy std::functionjest pusty. Rozważmy ten przykład:

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

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

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

Ten kod kompiluje się dobrze w MSVC, ale jeśli zadzwonię doSomething()bez inicjalizacji eventFunckodu, oczywiście ulega awarii. To jest oczekiwane, ale zastanawiałem się, jaka jest wartość eventFunc? Debugger mówi 'empty'. Naprawiłem to za pomocą prostej instrukcji if:

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

To działa, ale wciąż zastanawiam się, jaka jest wartość braku zainicjowania std::function? Chciałbym pisać, if (eventFunc != nullptr)ale std::function(oczywiście) nie jest wskaźnikiem.

Dlaczego czysty, jeśli działa? Jaka magia się za tym kryje? I czy to właściwy sposób, jak to sprawdzić?

NightElfik
źródło
8
Zauważ, że eventFuncto nie jest lambda; to jest std::function. Możesz przechowywać lambdy w std::functions, ale to nie to samo.
templatetypedef
3
Masz rację, zmieniłem tytuł, aby uniknąć nieporozumień. Dzięki.
NightElfik

Odpowiedzi:

107

Nie sprawdzasz, czy nie ma pustej lambdy, ale czy std::functionma ona zapisaną wartość docelową do wywołania . Sprawdzenie jest dobrze zdefiniowane i działa, dzięki std::function::operator boolczemu umożliwia niejawną konwersję do boolkontekstów, w których wymagane są wartości logiczne (takie jak wyrażenie warunkowe w ifinstrukcji).

Poza tym pojęcie pustej lambdy nie ma sensu. W tle kompilator konwertuje wyrażenie lambda na definicję struct(lub class), a przechwycone zmienne są przechowywane jako elementy składowe this struct. Zdefiniowano również operator wywołania funkcji publicznej, co pozwala na wywołanie lambdy. Więc czym byłaby pusta lambda?


Możesz także napisać, if(eventFunc != nullptr)jeśli chcesz, jest to odpowiednik kodu, który masz w pytaniu. std::function definiuje operator== i operator!=przeciąża do porównywania z nullptr_t.

Pretorianin
źródło
1
== nullptrJednak nie robi tego samego? Wygląda na to ==, że operator powinien mieć przeciążenie , które powoduje „puste” std::functionporównanie truez nullptr: cplusplus.com/reference/functional/function/operators
Kyle Strand
3
@KyleStrand Tak, porównanie do nullptrwill również zadziała, if(eventFunc != nullptr)jest równoważne if(eventFunc)w powyższym pytaniu.
Praetorian
3
Technicznie std::function::operator boolnie zezwala na niejawną konwersję do bool. W explicitkońcu jest zaznaczony , ale standard robi wyjątek dla pewnych konstrukcji językowych, które oczekują wyrażeń boolowskich, nazywając go „kontekstowo konwertowanym na bool”. Odpowiedni fragment kodu
bcrist
@bcrist Tak, zdaję sobie sprawę, że operator konwersji boolowskiej to explicit, dlatego uważałem, że zezwala na niejawną konwersję do boolkontekstów, w których wymagane są wartości logiczne . Dokładnie to dzieje się w omawianym kodzie.
Praetorian
5
@Praetorian Chodzi o to, że standard przypisuje bardzo specyficzne znaczenie frazie „niejawna konwersja” i różni się ona wyraźnie od „kontekstowej konwersji na bool”, która również ma bardzo specyficzne znaczenie. Nie ma tu związku „jest-a”. Rozumiem, że początkujący prawdopodobnie nie muszą od razu znać różnicy między konwersją niejawną / jawną / kontekstową, ale lepiej jest nauczyć się odpowiednich słów podświadomie, niż później przełamać stare nawyki.
bcrist
22

Sprawdź tutaj http://www.cplusplus.com/reference/functional/function/operator_bool/

Przykład

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

Wynik

foo nie jest wywoływalne.

bar jest wywoływalny.

Dawid Drozd
źródło
31
Myślę, że ta odpowiedź byłaby bardziej jasna bez swap(). Myślałem, że wynik jest wstecz, dopóki nie zdałem sobie z tego sprawy.
cp.engr
0

(Pozwól, że udzielę jasnej odpowiedzi.)

Możesz sprawdzić, czy std::functionjest pusty za pomocą std::function::operator bool.

prawda: jeśli obiekt jest wywoływalny.
false: w przeciwnym razie (obiekt jest pustą funkcją)

Przykład

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

Wynik

foo nie jest puste.
pasek jest pusty.

zwcloud
źródło
2
Twoje ciągi wynikowe są zamieniane.
Sophit
@Sophit Czy na pewno? ;)
zwcloud
1
Twój komentarz mówi, że foo nie jest puste, a wynik się nie zgadza. Zgadzam się z twoim komentarzem.
Sophit