10
#include <iostream>
#include <functional>

int global = 9;
std::function<void()> functor;

int main()
{
    int* ptr = &global;
    functor = [ptr]
    {
        functor = nullptr;
        std::cout << *ptr << std::endl;
    };

    functor();
}

Here is variable ptr captured by lambda, and during functor() call functor first deleted through functor = nullptr and then accesses ptr. I think that ptr was corrupted since it was a field of a deleted functor. All the compilers successfully performs that program without crashes and print "9", but I still doubt that this is not undefined behavior. Can someone confirm it?

Denis
  • 2,786
  • 1
  • 14
  • 29
  • @keith I don't think that's quite a duplicate. That question is about the deletion itself and this one seems to be focused on the lamba's captured `ptr`. – François Andrieux Mar 05 '18 at 16:40
  • 1
    https://stackoverflow.com/questions/17347103/remove-self-from-the-container-in-the-lambda similar? – An0num0us Mar 05 '18 at 16:42
  • @Whatever: Yes, that's an exact duplication (a `std::function` is a container with maximum size 1) – Ben Voigt Mar 05 '18 at 16:44

1 Answers1

8

It is indeed undefined.

Here is how you can confirm it:

#include <iostream>
#include <functional>

struct S
{
    ~S() {std::cout << "~S()\n";}
};

std::function<void()> functor;

int main()
{
    functor = [s = S()]
    {
        functor = nullptr;
        std::cout << "End of lambda\n";
    };

    functor();
}

The code above prints (on GCC and Clang):

~S()
~S()
~S()
End of lambda

The 3 destroyed instances are: one captured by the original lambda, one captured by a copy stored in functor and one captured by a temporary that std::function::operator=(F &&) for some reason has to make.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • 1
    Actually you see three destructors because function::operator=(F&&) is implemented in terms of swap, which creates additional copy of lambda. To verify this you can make f a local variable, and initialize it during declaration - you will see only 2 S destructors calls then. – Maciej Cencora Mar 07 '18 at 12:54