Como verificar corretamente se std :: function está vazio em C ++ 11?

95

Eu queria saber como verificar corretamente se um std::functionestá vazio. Considere este exemplo:

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 compila perfeitamente no MSVC, mas se eu chamar doSomething()sem inicializar eventFunco código obviamente falha. Isso é esperado, mas eu queria saber qual é o valor do eventFunc? O depurador diz 'empty'. Então, resolvi isso usando uma instrução if simples:

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

Isso funciona, mas ainda estou me perguntando qual é o valor de não inicializado std::function? Eu gostaria de escrever, if (eventFunc != nullptr)mas std::function(obviamente) não é um indicador.

Por que o puro se funciona? Qual é a magia por trás disso? E é a maneira correta de verificar isso?

NightElfik
fonte
8
Observe que eventFuncnão é um lambda; é um std::function. Você pode armazenar lambdas em std::functions, mas eles não são a mesma coisa.
templatetypedef de
3
Você está certo, mudei o título para evitar confusão. Obrigado.
NightElfik de

Respostas:

104

Você não está verificando se há um lambda vazio, mas se std::functionhá um destino que pode ser chamado armazenado nele. A verificação é bem definida e funciona porque std::function::operator boolpermite a conversão implícita boolem contextos onde os valores booleanos são necessários (como a expressão condicional em uma ifinstrução).

Além disso, a noção de um lambda vazio realmente não faz sentido. Nos bastidores, o compilador converte uma expressão lambda em uma definição struct(ou class), com as variáveis ​​que você captura armazenadas como membros de dados dela struct. Um operador de chamada de função pública também é definido, o que permite que você invoque o lambda. Então, o que seria um lambda vazio?


Você também pode escrever if(eventFunc != nullptr)se quiser, é equivalente ao código que você tem na pergunta. std::function define operator== e operator!=sobrecarrega para comparar com a nullptr_t.

Pretoriano
fonte
1
Mas não == nullptrfaz a mesma coisa? Parece que não é suposto ser uma sobrecarga para o ==operador que provoca um "vazio" std::functionpara comparar truecontra nullptr: cplusplus.com/reference/functional/function/operators
Kyle Strand
3
@KyleStrand Sim, comparando com nullptrfuncionará também, if(eventFunc != nullptr)é equivalente a if(eventFunc)na pergunta acima.
Pretoriano de
3
Tecnicamente, std::function::operator boolnão permite a conversão implícita em bool. explicitAfinal, ele está marcado , mas o padrão abre uma exceção para certas construções de linguagem que esperam expressões booleanas, chamando-o de "convertido contextualmente em bool". Você pode encontrar o trecho relevante do standardese e uma explicação aqui: chris-sharpe.blogspot.com/2013/07/…
bcrist
@bcrist Sim, estou ciente de que o operador de conversão booleana é explicit, é por isso que tive o cuidado de afirmar que permite a conversão implícita boolem contextos onde os valores booleanos são necessários . Isso é exatamente o que está acontecendo no código em questão.
Pretoriano de
5
@Praetorian O que estou tentando enfatizar é que o padrão atribui um significado muito específico à frase "conversão implícita" e é distintamente diferente de "conversão contextual em bool", que também tem um significado muito específico. Não há nenhuma relação "É-um" aqui. Eu entendo que iniciantes provavelmente não precisam saber a diferença entre conversão implícita / explícita / contextual imediatamente, mas é melhor aprender as palavras certas subconscientemente, do que quebrar velhos hábitos mais tarde.
bcrist de
21

Verifique aqui http://www.cplusplus.com/reference/functional/function/operator_bool/

Exemplo

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

Resultado

foo não pode ser chamado.

bar é exigível.

Dawid Drozd
fonte
31
Acho que essa resposta seria mais clara sem o swap(). Eu estava pensando que a saída estava ao contrário até que percebi.
cp.engr
-1

(Deixe-me dar uma resposta clara.)

Você pode verificar se a std::functionestá vazio com std::function::operator bool.

verdadeiro: se o objeto for chamável.
falso: caso contrário (o objeto é uma função vazia)

Exemplo

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

Resultado

foo não está vazio.
a barra está vazia.

zwcloud
fonte
2
Suas strings de resultados são trocadas.
Sophit de
@Sophit Tem certeza? ;)
zwcloud
1
Seu comentário diz que foo não está vazio e a saída discorda. Eu concordo com o seu comentário.
Sophit