When the object of std::function
is destroyed?
Why the pointer to the object of std::function
is still valid when the variable fn1
is out of the scope(you see the code snippet works well, http://cpp.sh/6nnd4)?
// function example
#include <iostream> // std::cout
#include <functional> // std::function, std::negate
// a function:
int half(int x) {return x/2;}
int main () {
std::function<int(int)> *pFun;
{
std::function<int(int)> fn1 = half; // function
pFun= &fn1;
std::cout << "fn1(60): " << (*pFun)(60) << '\n';
}
std::cout << "fn1(60): " << (*pFun)(90) << '\n';
return 0;
}
Why the pointer to the object of std::function is still valid when the variable fn1 is out of the scope?
Let me present a simpler example, using ints. But if you are brave, you can try to read the assembler for the std::function
version.
int main () {
int a = 0;
int *c = nullptr;
{
int b = 1;
c = &b;
}
a = *c;
return a;
}
This is generated with gcc 10.2 -O0, but the other compilers have a really similar output. I will comment it to aid the understanding.
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 0 # space for `a`
mov QWORD PTR [rbp-16], 0 # space for `c`
mov DWORD PTR [rbp-20], 1 # space for `b`
lea rax, [rbp-20] # int *tmp = &b Not really, but enough for us
mov QWORD PTR [rbp-16], rax # c = tmp
mov rax, QWORD PTR [rbp-16]
mov eax, DWORD PTR [rax] # int tmp2 = *tmp
mov DWORD PTR [rbp-4], eax # a = tmp2
mov eax, DWORD PTR [rbp-4]
pop rbp
ret # return a
And the program return 1
, as expected when you see the assembler. You can see, b was not "invalidated", because we did not roll the stack back, and we didnt change its value. This will be a case like the one you are in, were it works.
Now lets enable optimizations. Here is it compiled with -O1
, the lowest level.
Here is it compiled with gcc:
main:
mov eax, 0
ret
And here is it compiled with clang:
main: # @main
mov eax, 1
ret
And here is with visual studio:
main PROC ; COMDAT
mov eax, 1
ret 0
main ENDP
You can see the results are diferent. It is undefined behaviour, each implementation will do as it sees fit.
Now this is happening with some local variables in the same function. But consider this cases:
It was a pointer, lets say allocated with new. You already called delete. What guaranties that that memory is not used by someone else now?
When the stack grows, the value will eventually be overiden. Consider this code:
int* foo() {
int a = 0;
return &a;
}
void bar() {
int b = 1;
}
int main () {
int *ptr = foo();
bar();
int a = *ptr;
return a;
}
It didnt return 1 or 0. It returned 139.
And here is a good read on this.