0

Let's imagine I define a naive function reference wrapper like follows :

struct wrapper {
    std::function<void(void)>& fct;
    
    wrapper(std::function<void(void)>& _fct) : fct{_fct}{
        fct();    
    };
};

Of course, in reality it is more complex than this, but I simplified it to illustrate the problem.

Then, I would like to create my test object which contains the function reference wrapper :

struct test {
    wrapper myWrapper;
    
    test() : myWrapper{std::bind(&test::boom, this)}{};
    
    void boom() {
        std::cout << "boom()" << std::endl;
    }
    
};

At test object instanciation, I get the following error (try yourself here) :

 cannot bind non-const lvalue reference of type 'std::function<void()>&' to an rvalue of type 'std::function<void()>'

It is understandable, since the std::bind object is a temporary (rvalue) for which I cannot take a reference (I also tried my way without std::bind, without better results). On the other hand, being able to take a reference to a non-static member function as a std::function seems like something easy to do, but I can't wrap my head around it.

Is there any simple way to take a reference to a non-static member function as a std::function&?

Magix
  • 4,989
  • 7
  • 26
  • 50
  • 1
    That's because lvalue reference can't bind to temporaries (and your `bind` return value is a temporary). Why are you doing references at all? Just make a copy of a function object. If you must take a reference, get a const lvalue reference, or an lvalue reference. – SergeyA Jul 17 '20 at 21:45
  • You don't need a reference to a `std::function`. The `std::function` itself will "reference" the fucntion you want to call – NathanOliver Jul 17 '20 at 21:46
  • Tthe real-life use-case for this is through embedded RTOS multithreading + CRTP. Thus, I cannot bear the cost of copying a std::function (see https://stackoverflow.com/questions/44388849). I simplified it a lot here :) – Magix Jul 17 '20 at 21:46
  • Also, without considering the `std::function` copy cost, it is the same problem with simple function pointers : http://coliru.stacked-crooked.com/a/8e66dac0d352ca03 – Magix Jul 17 '20 at 21:56
  • 1
    Can you bear the cost of moving it? That's the simplest way out. And compiler should optimize the move away, if it can see the endpoint (assigning to class member) - inline constructor declaration in header. If not, you can use references, but you need to ensure `std::bind` result will outlive your class object. – Yksisarvinen Jul 17 '20 at 21:57
  • The same problem would be with any type. You cannot bind non-const reference to temporary. – Yksisarvinen Jul 17 '20 at 21:57
  • Yeah, I could move. I'm not sure I understand what you mean by "You cannot bind non-const reference to temporary" – Magix Jul 17 '20 at 22:02
  • 1
    Exactly what the error message is saying ;) Non-const reference can only be assigned to a named object, assigning to temporary wouldn't have sense - what is the point in making changes to object that lives only in current function? Const references however can be bound to a temporary (unnamed) object, and they will prolong the lifetime of that object until function returns. This lets you use const ref parameter with both lvalues and rvalues. This does not extend to class members, so you can't use this trick to initialize `fct` with temporary. – Yksisarvinen Jul 17 '20 at 22:12
  • @Yksisarvinen this is official rationale, but I could never understand it. There was no real reason to block binding of lvalue references to temporaries pre-11 (it happened to turn out for better with rvalue references, but back in 2003 nobody predicted that). In 2003 not being able to bind non-const refs to temporaries was a short-sighted and impeded some good code. – SergeyA Jul 17 '20 at 22:16
  • @Magix are you sure about it? How big your function object is, you think? Did you compare the cost of copying function with the cost of extra indirection through reference? – SergeyA Jul 17 '20 at 22:18
  • I just managed to solve it through using class member lambdas and https://stackoverflow.com/questions/7852101 ! – Magix Jul 17 '20 at 22:27
  • @SergeyA I'm afraid I cannot discuss this matter, I learnt C++ starting from C++14 mostly and it seemed like a reasonable rationale (I didn't even know that it's official). I suppose there could be some good uses cases like ignoring output parameters, but... It feels like it would a mistake more often than not. Preventing people from doing mistakes seems quite out of C++ spirit tho (at least if there can be a legitimate reason for that mistake). – Yksisarvinen Jul 17 '20 at 22:28

1 Answers1

0

With this "raw" implementation of "typed bind" I can ask : Where the memory that holds "test*" pointer would be stored by wrapper ? And how bindex would know wich type sould be converted in where call ->first ? Should this bindex be type erased ?

struct bindx : pair<void(test::*)(void), test*> {
    using pair<void(test::*)(void), test*>::pair;
    void operator()() { (second->*first)(); }
};

struct wrapper {
    bindx f_;
    
    wrapper(bindx f) : f_{f}{
        f_();    
    };
};

struct test {
    wrapper myWrapper;
    
    test() : myWrapper( bindx{&test::boom, this} ) {};
     . . .
}

http://coliru.stacked-crooked.com/a/a40f432aad928908