5

Can someone help me to understand why the following code causes 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 _fand consequently, I thought func() is not a temporary. It would be great if someone helps me understand.

demongolem
  • 9,474
  • 36
  • 90
  • 105
abraham_hilbert
  • 2,221
  • 1
  • 13
  • 30

4 Answers4

5

The problem isn't the use of std::function, its that you're trying to return the temporary float from func() as a reference. This won't work since the object would cease to exist as soon as the statement ends.

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

Kiskae
  • 24,655
  • 2
  • 77
  • 74
3

I think you understand that returning a reference to a local variable isn't valid once the variable goes out of scope. What you seem to be missing though is that std::function<float()> func = a; actually creates a local std::function from a. It doesn't point to a in any way, func has it's own A. Which means that calling func(); doesn't actually invoke a.operator() but rather the A of func. Then we get back to the local variable returning a reference is evil part.

To make it compile, you can change your template signature to float&() but it's still undefined behaviour.

A fix would be to change the return type to a copy instead (to auto), removing the reference.

Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
  • Why `std::function func = a;` causes no problem? Isn't `A`'s operator signature `float&`? Is it because they are compatible? – Marson Mao Oct 24 '16 at 09:36
  • @MarsonMao It does cause a problem, a compilation problem that can be fixed by changing to `float&()`. However, this still causes UB. Which is why we can't return by reference. – Hatted Rooster Oct 24 '16 at 09:38
  • I can compile and execute it fine with `auto foo()` and `std::function func = a;`. I found this interesting. Could you please explain why? (I'm using VS2015) – Marson Mao Oct 24 '16 at 09:40
  • @MarsonMao Because it makes a copy of the reference returned in that case. Good eye though. – Hatted Rooster Oct 24 '16 at 09:43
  • I did some test and found that when you are returning `auto foo()`, you can have both ` f = a;` and ` = a;` working. This is kind of weird since it should only allow ` = a;` isn't it? I mean because the signature is `float& operator()()`, so why does `` also work? – Marson Mao Oct 24 '16 at 09:52
1

For std::function<float()> func, you're declaring func as a functor returning a float, not a float&. As the error message said, the temporary float returned by func() can't be bound to non-const lvalue reference.

The above declaration doesn't match the signature of A::operator() which being wrapped. But note that if change the type to std::function<float&()> func to match the signature of A::operator(), the compile error could be sovled, but then we'll return a reference bound to local variable, which leads to UB.

Note that for std::function<float()> func = a;, std::function is initialized with a copy of a. Then func() will return a reference bound to member of A wrapped in func, which is a local variable. And the reference will dangle when get out of function foo.

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

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • I thought so too, because it then compiles fine, but I'm afraid it's UB, because it does not seem to run correctly (e.g. it crashes or prints 0). – Christian Hackl Oct 24 '16 at 09:31
  • I found this question interesting: if you make it `auto&` and ``, it can work; if you make it `auto` and `` it also works. Which one is correct? I feel that the `auto` and `` one is correct since you shouldn't return local variable by reference, right? – Marson Mao Oct 24 '16 at 09:32
  • @MarsonMao Yes, we shouldn't return reference bound to local, the point is `foo` should return by copy here. (It's still unknown why OP needs return by reference.) – songyuanyao Oct 24 '16 at 09:46
1

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

I guess OP really wants to return a float& of a certain object (which is a in OP's example).

So if OP wants foo to return auto& (which should be a float&), then it should be as the following, please note the std::bind part:

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's address
    float& f = T1::foo(); // note that `a`'s 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's 777

    return 0;
}
Marson Mao
  • 2,935
  • 6
  • 30
  • 45