6

If i have an instance of std::function that is bound to a member function of an object instance and that object instance goes out of scope and is otherwise destroyed will my std::function object now be considered to be a bad pointer that will fail if called?

Example:

int main(int argc,const char* argv){
    type* instance = new type();
    std::function<foo(bar)> func = std::bind(type::func,instance);
    delete instance;
    func(0);//is this an invalid call
}

Is there something in the standard that specifies what should happen? My hunch is that it will throw and exception because the object no longer exists

EDIT: Does the standard specify what should happen?

Is it undefined behavior?

EDIT 2:

#include <iostream>
#include <functional>
class foo{
public:
    void bar(int i){
        std::cout<<i<<std::endl;
    }
};

int main(int argc, const char * argv[]) {
    foo* bar = new foo();
    std::function<void(int)> f = std::bind(&foo::bar, bar,std::placeholders::_1);
    delete bar;
    f(0);//calling the dead objects function? Shouldn't this throw an exception?

    return 0;
}

Running this code i receive an output value of 0;

Alex Zywicki
  • 2,263
  • 1
  • 19
  • 34

1 Answers1

7

What will happen is undefined behavior.

The bind() call will return some object that contains a copy of instance, so that when you call func(0) will effectively call:

(instance->*(&type::func))(0);

Dereferencing an invalid pointer, as you would do there if instance were deleted, is undefined behavior. It will not throw an exception (although, it's undefined, so it could, who knows).

Note that you're missing a placeholder in your call:

std::function<foo(bar)> func = 
    std::bind(type::func, instance, std::placeholders::_1);
//                                  ^^^^^^^ here ^^^^^^^^^

Without that, you can't call func(0) even with a non-deleted instance.

Updating your example code to better illustrate what's going on:

struct foo{
    int f;
    ~foo() { f = 0; }

    void bar(int i) {
        std::cout << i+f << std::endl;
    }
};

With that added destructor, you can see the difference between copying the pointer (in f) and copying the object that was pointed to (in g):

foo* bar = new foo{42};
std::function<void(int)> f = std::bind(&foo::bar, bar, std::placeholders::_1);
std::function<void(int)> g = std::bind(&foo::bar, *bar, std::placeholders::_1);
f(100); // prints 142
g(100); // prints 142
delete bar;
f(100); // prints 100
g(100); // prints 142 still, because it has a copy of
        // the object bar pointed to, rather than a copy
        // of the pointer
Barry
  • 286,269
  • 29
  • 621
  • 977
  • @ Barry The code was just an example, but yes thanks the placeholder is needed. Is there somewhere in the standard i can reference for that? – Alex Zywicki Jan 20 '15 at 20:32
  • 1
    @AlexZywicki Check out [this old question](http://stackoverflow.com/questions/4285895/where-exactly-does-c-standard-say-dereferencing-an-uninitialized-pointer-is-un) – Barry Jan 20 '15 at 20:40
  • At least in the case of a POD pointer type the behavior would be undefined. Does the `std::function` type resolve to a pointer internally? – Alex Zywicki Jan 20 '15 at 20:48
  • @AlexZywicki You can think of `std::function` as having some internal private type `placeholder` with `virtual R call(Args...) = 0;` that will be implemented by some specific `holder` which holds the `T` you construct it with. Regardless of how your library implements it, it's a reasonable mental model for how `std::function` works. – Barry Jan 20 '15 at 20:55
  • So does it copy the instance of the object you bind it to or does it hold a pointer/reference to that object. I just tried it out and it does not break in any way if the object no longer exists... Which leads me to believe that it is making a copy. – Alex Zywicki Jan 20 '15 at 21:00
  • @AlexZywicki It copies the *pointer* - not the object. If you want to copy the object you have to call `bind(&some_method, *instance)`. That will avoid the dangling pointer. You can verify yourself with a simple example by making the destructor of your type zero out (or otherwise change) member values. – Barry Jan 20 '15 at 21:05
  • @AlexZywicki Updated my answer - hope that clarifies things. – Barry Jan 20 '15 at 21:17
  • What I'm confused about is how the last two function call are even valid because the object `bar` to which the `std::function` objects were bound to no longer exists thus making it an invalid pointer to the objects `bar`, which in my mind should throw just as if i used a regular pointer that was dead. – Alex Zywicki Jan 20 '15 at 21:23
  • @AlexZywicki It shouldn't throw. It's just undefined behavior that happens to do something valid-ish. This code, using my `foo`, prints 5: `foo* bar2 = new foo{100}; delete bar2; bar2->bar(5);`. – Barry Jan 20 '15 at 21:34
  • It seems to be ignoring the instance variable or using them as uninitialized data. – Alex Zywicki Jan 20 '15 at 21:49
  • @AlexZywicki I believe that it calls what is supposed to be a method `bar` of the object which supposed to be pointed by pointer `bar`. The reason why it executes normally even after `delete bar` is that this memory hasn't been corrupted/overwritten (yet) and the object as well as all it's ivars still stay "there". Problems will occur when you have a lot of other things going on in your code. – peetonn Jan 22 '15 at 08:20
  • @peetonn so essentially i have a dangling pointer to a piece of memory that i no longer own but still has semi-valid data for now? – Alex Zywicki Jan 26 '15 at 02:05
  • @AlexZywicki no, you shouldn't rely on this memory at all as it's not guaranteed what's in there. it could be valid, could be not. consider the worst. – peetonn Jan 26 '15 at 07:03
  • @peetonn I didn't plan on relying on it, i was just restating what you said in a way that I understood. – Alex Zywicki Jan 27 '15 at 01:05