132

I was wondering how to properly check if an std::function is empty. Consider this example:

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

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

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

This code compiles just fine in MSVC but if I call doSomething() without initializing the eventFunc the code obviously crashes. That's expected but I was wondering what is the value of the eventFunc? The debugger says 'empty'. So I fixed that using simple if statement:

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

This works but I am still wondering what is the value of non-initialized std::function? I would like to write if (eventFunc != nullptr) but std::function is (obviously) not a pointer.

Why the pure if works? What's the magic behind it? And, is it the correct way how to check it?

Praetorian
  • 106,671
  • 19
  • 240
  • 328
NightElfik
  • 4,328
  • 5
  • 27
  • 34

3 Answers3

138

You're not checking for an empty lambda, but whether the std::function has a callable target stored in it. The check is well-defined and works because of std::function::operator bool which allows for implicit conversion to bool in contexts where boolean values are required (such as the conditional expression in an if statement).

Besides, the notion of an empty lambda doesn't really make sense. Behind the scenes the compiler converts a lambda expression into a struct (or class) definition, with the variables you capture stored as data members of this struct. A public function call operator is also defined, which is what allows you to invoke the lambda. So what would an empty lambda be?


You can also write if(eventFunc != nullptr) if you wish to, it's equivalent to the code you have in the question. std::function defines operator== and operator!= overloads for comparing with a nullptr_t.

Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • 1
    Doesn't `== nullptr` do the same thing, though? It looks like there's supposed to be an overload for the `==` operator that causes an "empty" `std::function` to compare `true` against `nullptr`: http://www.cplusplus.com/reference/functional/function/operators/ – Kyle Strand Jun 20 '15 at 00:54
  • 3
    @KyleStrand Yes, comparing to `nullptr` will work too, `if(eventFunc != nullptr)` is equivalent to `if(eventFunc)` in the question above. – Praetorian Jun 20 '15 at 01:09
  • 5
    Technically, `std::function::operator bool` does not allow implicit conversion to `bool`. It is marked `explicit` after all, but the standard makes an exception for certain language constructs that expect boolean expressions, calling it "contextually converted to bool." You can find the relevant snippet of standardese and an explanation here: http://chris-sharpe.blogspot.com/2013/07/contextually-converted-to-bool.html – bcrist Jun 20 '15 at 06:57
  • 2
    @bcrist Yep, I'm aware that the boolean conversion operator is `explicit`, that's why i was careful to state *allows for implicit conversion to `bool` in contexts where boolean values are required*. This is exactly what's happening in the code in question. – Praetorian Jun 20 '15 at 07:18
  • 7
    @Praetorian The point I'm trying to make is that the standard assigns a very specific meaning to the phrase "implicit conversion", and it is distinctly different from "contextual conversion to bool," which also has a very specific meaning. There is no "Is-a" relationship here. I understand that beginners probably don't need to know the difference between implicit/explicit/contextual conversion right away, but it's better to learn the right words subconsciously, than have to break old habits later. – bcrist Jun 20 '15 at 14:40
37

Check here for std::function::operator bool

Return value

  • true if the object is callable.
  • false otherwise (the object is an empty function).

Example:

// function::operator bool example
#include <iostream>     // std::cout
#include <functional>   // std::function, std::plus

int main ()
{
    std::function<int(int,int)> foo; // empty
    
    if(foo)
    {
        std::cout << "[1] foo is NOT empty" << std::endl;
    }
    else
    {
        std::cout << "[1] foo is EMPTY" << std::endl;
    }
    
    // Now we can assign example
    foo = std::plus<int>();
  
    if(foo)
    {
        std::cout << "[2] foo is NOT empty" << std::endl;
    }
    else
    {
        std::cout << "[2] foo is EMPTY" << std::endl;
    }
  
    // Example use with ternary operator (https://www.cprogramming.com/reference/operators/ternary-operator.html)
    std::cout << "[3] foo is " << (foo ? "callable" : "NOT callable") << std::endl;
    
    // make it empty
    foo = {};
    std::cout << "[4] foo is " << (foo ? "callable" : "NOT callable") << std::endl;
    
    foo = [](int a, int b){ return a+b; };
    std::cout << "[5] foo is " << (foo ? "callable" : "NOT callable") << std::endl;
    
    return foo(1, 1) == 2; // return 1
}

Output

[1] foo is EMPTY
[2] foo is NOT empty
[3] foo is callable
[4] foo is NOT callable
[5] foo is callable
user2399321
  • 605
  • 7
  • 13
Gelldur
  • 11,187
  • 7
  • 57
  • 68
  • 35
    I think this answer would be more clear without the `swap()`. I was thinking the output was backwards until I realized it. – cp.engr Apr 17 '18 at 14:05
  • Isn't [cplusplus.com](http://www.cplusplus.com) much much better than [cppreference.com](https://cppreference.com)? I really find the former examples much more clear. Why so many people turned to latter? I wonder! – Vassilis Mar 07 '22 at 15:05
  • @Vassilis cplusplus did not have documentation for newer C++ standards for a long time. cppreference is up-to-date and have formal definitions. I suggest using cplusplus at learning phase. – Burak Nov 08 '22 at 17:12
7

(Let me provide a clear answer.)

You can check if a std::function is empty with std::function::operator bool.

true: if the object is callable.
false: otherwise (the object is an empty function)

Example

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

Output

foo is not empty.
bar is empty.

zwcloud
  • 4,546
  • 3
  • 40
  • 69