¿Cómo verificar correctamente si la función std :: está vacía en C ++ 11?

96

Me preguntaba cómo comprobar correctamente si un std::functionestá vacío. Considere este ejemplo:

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

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

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

Este código se compila bien en MSVC, pero si llamo doSomething()sin inicializar eventFuncel código obviamente se bloquea. Eso es lo esperado, pero me preguntaba cuál es el valor de eventFunc? El depurador dice 'empty'. Así que lo arreglé usando una simple declaración if:

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

Esto funciona, pero todavía me pregunto cuál es el valor de no inicializado std::function. Me gustaría escribir if (eventFunc != nullptr)pero std::function(obviamente) no es un puntero.

¿Por qué el puro si funciona? ¿Cuál es la magia detrás de esto? Y, ¿es la forma correcta de comprobarlo?

NightElfik
fuente
8
Tenga en cuenta que eventFuncno es una lambda; es un std::function. Puede almacenar lambdas en std::functions, pero no son lo mismo.
templatetypedef
3
Tienes razón, cambié el título para evitar confusiones. Gracias.
NightElfik

Respuestas:

104

No está comprobando una lambda vacía, sino si std::functiontiene un objetivo invocable almacenado en él. La verificación está bien definida y funciona, por lo std::function::operator boolque permite la conversión implícita boolen contextos donde se requieren valores booleanos (como la expresión condicional en una ifdeclaración).

Además, la noción de una lambda vacía realmente no tiene sentido. Detrás de escena, el compilador convierte una expresión lambda en una struct(o class) definición, con las variables capturadas almacenadas como miembros de datos de esta struct. También se define un operador de llamada de función pública, que es lo que le permite invocar la lambda. Entonces, ¿qué sería una lambda vacía?


También puede escribir if(eventFunc != nullptr)si lo desea, es equivalente al código que tiene en la pregunta. std::function define operator== y operator!=sobrecarga para comparar con a nullptr_t.

Pretoriano
fuente
1
¿ == nullptrPero no hace lo mismo? Parece que se supone que hay una sobrecarga para el ==operador que causa un "vacío" std::functionpara comparar truecon nullptr: cplusplus.com/reference/functional/function/operators
Kyle Strand
3
@KyleStrand Sí, la comparación con nullptrfuncionará también, if(eventFunc != nullptr)es equivalente a if(eventFunc)en la pregunta anterior.
Pretoriano
3
Técnicamente, std::function::operator boolno permite la conversión implícita a bool. explicitDespués de todo, está marcado , pero el estándar hace una excepción para ciertas construcciones de lenguaje que esperan expresiones booleanas, llamándolo "convertido contextualmente a bool". Puede encontrar el fragmento relevante de standardese y una explicación aquí: chris-sharpe.blogspot.com/2013/07/…
bcrist
@bcrist Sí, soy consciente de que el operador de conversión booleano es explicit, por eso tuve cuidado de indicar que permite la conversión implícita boolen contextos donde se requieren valores booleanos . Esto es exactamente lo que sucede en el código en cuestión.
Pretoriano
5
@Praetorian El punto que estoy tratando de hacer es que el estándar asigna un significado muy específico a la frase "conversión implícita", y es claramente diferente de "conversión contextual a bool", que también tiene un significado muy específico. Aquí no hay una relación "Es-a". Entiendo que los principiantes probablemente no necesiten saber la diferencia entre conversión implícita / explícita / contextual de inmediato, pero es mejor aprender las palabras correctas de manera subconsciente que tener que romper los viejos hábitos más tarde.
bcrist
21

Consulte aquí http://www.cplusplus.com/reference/functional/function/operator_bool/

Ejemplo

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

Salida

foo no se puede llamar.

la barra es invocable.

Dawid Drozd
fuente
31
Creo que esta respuesta sería más clara sin el swap(). Estaba pensando que la salida estaba al revés hasta que me di cuenta.
cp.engr
-1

(Déjame darte una respuesta clara).

Puede comprobar si a std::functionestá vacío con std::function::operator bool.

verdadero: si el objeto es invocable.
falso: de lo contrario (el objeto es una función vacía)

Ejemplo

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

Salida

foo no está vacío.
la barra está vacía.

zwcloud
fuente
2
Se intercambian las cadenas de resultados.
Sophit
@Sophit ¿Estás seguro? ;)
zwcloud
1
Su comentario dice que foo no está vacío y la salida no está de acuerdo. Estoy de acuerdo con tu comentario.
Sophit