Using std :: function to wrap a function object

Can someone help me understand why the following code is causing an error?

class A { public: float& operator()() { return _f; } private: float _f = 1; } a; auto& foo() { std::function<float()> func = a; return func(); } int main() { std::cout << foo() << std::endl; } 

Error:

 error: non-const lvalue reference to type 'float' cannot bind to a temporary of type 'float' return func(); ^~~~~~ 1 error generated. 

Here, in operator() , I return a reference to _f , and therefore I thought func() not temporary. It would be great if someone helped me understand.

+5
source share
4 answers

For std::function<float()> func you declare func as a functor that returns a float , not a float& . As the error message says, the temporary float returned by func() cannot be bound to the non-console lvalue reference.

The above declaration does not match the signature of A::operator() , which ends. But keep in mind that if you change the type to std::function<float&()> func so that it matches the signature of A::operator() , the compilation error could be overturned, but then we will return the link to the local variable, which will lead to UB.

Note that for std::function<float()> func = a; std :: function is initialized with a copy of a . Then func() will return the link associated with member a wrapped in func , which is a local variable. And the link will dangle when you exit the foo function.

How to fix this depends on your design, change auto& foo() to auto foo() , i.e. passing the return value by copying will avoid UB here.

+1
source

The problem is not using std::function , but that you are trying to return a temporary float from func() as a reference. This will not work, since the object will cease to exist as soon as the statement ends.

If you change auto& foo() to auto foo() , it should work.

+5
source

I think you understand that returning a reference to a local variable is not valid after the variable goes out of scope. What seems to you to be missing is that std::function<float()> func = a; actually creates a local std::function from a . It does not indicate a in any way, func has its own a . This means that calling func(); doesn't actually call a.operator() , but rather a of func . Then we return to the local variable returning the link - this is the evil part.

To compile it, you can change the template signature to float&() , but still undefined.

The fix will be to instead change the return type to a copy (before auto ) by deleting the link.

+3
source

After reading the great answers above, I tried to give a few different thoughts.

I think the OP really wants to return the float& specific object (which is a in the OP example).

So, if the OP wants foo to return auto& (which should be float& ), then it should be as follows: pay attention to the std::bind :

 namespace T1 { class A { public: float& operator()() { std::cout << "a add = " << this << std::endl; return _f; } float getF() { return _f; } private: float _f = 1; } a; auto& foo() { std::function<float&()> func = std::bind(&A::operator(), &a); return func(); } } // end of namespace T1 int main() { std::cout << "global a add = " << &(T1::a) << std::endl; // check a address float& f = T1::foo(); // note that `a` address is the same std::cout << f << std::endl; // still 1 f = 777; std::cout << f << std::endl; // now 777 std::cout << T1::a.getF() << std::endl; // it 777 return 0; } 
+1
source

Source: https://habr.com/ru/post/1258703/


All Articles