1

Bind a class member function and a valid class object pointer to boost::function<>fn. What may happen if invoking the fn after the object which the pointer pointed to has been destroyed? Are there some potential problems that I should be aware of? Domo code snappet:

 class CTest
 {
    public:
         int demo(void){}
 };

 
 int main()
 {
    boost::function<int(void)> fn;
    {
        CTest ins;
        fn = boost::bind(&CTest::demo, &ins); 
    }
    fn();
}

Edited(https://godbolt.org/z/r8EK1G)

Quoted from the comment of j6t

One way to do that is to pass the object by value, not a pointer to the object. Then a copy of the object would be used during the invocation fn(). I think there is still a problem that the object 'tes' is out of scope.So passing value is not a good method. :

 #include<functional>
 #include<iostream>

 class CTest
 {
    public:
         int demo(void){std::cout << "do better" << std::endl;return 0;}
 };

template <class T=CTest>
std::function<int(void)>  bindWarp(T obj, int (T::*mem_func)(void))
{
    return std::bind(mem_func, obj);
}

 int main()
 {
    std::function<int(void)> fn;
    {
        CTest tes;
        fn = bindWarp(tes, &CTest::demo);  
    }
    fn();  //I think there is still a problem that the object 'tes' is out of scope.So passing value is not a good method.
}
John
  • 1,029
  • 2
  • 14
  • 1
    Your program has undefined behavior. Anything can happen. – j6t Dec 13 '20 at 10:09
  • @j6t You mean I have to guarantee that the pointer (which points to an object) is still valid when invoking the `std::function` object? – John Dec 13 '20 at 10:11
  • Yes, of course. One way to do that is to pass the object by value, not a pointer to the object. Then a *copy* of the object would be used during the invocation `fn()`. – j6t Dec 13 '20 at 10:14
  • @j6t "Passing the object by value"? Not a reference? Do you mean this()? I think this code snippet has the same problem. godbolt.org/z/r8EK1G. I updated this code snippet to the post. – John Dec 13 '20 at 12:27
  • this is same weird behavior you can get in simpler way, without using a std::function. See [this example](https://godbolt.org/z/evYdrx); but as other are saying, you are in the realm of UB. In this case, it "works" because the `demo` function does not access `this`, (at least, for many compiler this can be the cause). but it's just that "anything can happen", even that it works – Gian Paolo Dec 13 '20 at 14:22

1 Answers1

1

You need to ensure that the target object's lifetime exceeds the function object's lifetime. This is easier to express in an obvious way with a lambda function instead of bind. The lambda can explicitely capture the object 'by-value':

std::function<int(void)> fn;
{
    CTest tes;
    fn = [tes] { tes.demo(); };
}
fn();

With std::bind you can also explicitely express this, and avoid a copy, by writing:

fn = std::bind(&CTest::demo, std::move(tes));

You could also pass to bind by-value instead of by-reference but I would prefer the two constructs above because they make the intention very clear (copying the object or transfering ownership).

If you want to avoid a copy or transfer of ownership, you may use a shared pointer with the lambda instead. You could even go as far as using a weak_ptr within the lambda, so the object is not kept alive for it.

See also this very helpful discussion about the details of binds & lambdas in modern C++: https://stackoverflow.com/a/17545183/21974

ypnos
  • 45,954
  • 14
  • 88
  • 130
  • When the object(passed by `std::move(tes)`) is destroyed? Does it have the same lifetime with `fn`? – John Dec 13 '20 at 12:25
  • 1
    The passed object resides within `fn` and is destroyed with it. From the documentation of std::bind: The return type of std::bind holds […] and one object per each of args..., of type std::decay::type. – ypnos Dec 13 '20 at 12:30
  • 2
    See also https://stackoverflow.com/questions/10018309/c-stdbind-keeping-object-alive – ypnos Dec 13 '20 at 12:34
  • Thank you very much. This link is very helpful. – John Dec 13 '20 at 12:47