Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

lvalue reference became invalid after passing through an identity function

Tags:

c++

c++11

Would someone explain why the reference became invalid after going through an "identity" function, foo1? Isn't an "address" to A is passed into and returned by foo1?

struct A {
    A(int x) : x_(x) {}
    int x_;
};

int main() {

    function<const A&(const A& r)> foo1 = [](const A& r) { 
        return r;
    };

    vector<A> vec{1, 2, 3};
    cout << foo1(vec[0]).x_ << endl;   // RUNTIME ERROR

    return 0;
}

How does the problem line differ from:

const A& r = vec[0];
const A& r1 = r;
like image 477
Candy Chiu Avatar asked Feb 10 '17 14:02

Candy Chiu


2 Answers

The problem is your lambda. It doesn't do what you think it does:

function<const A&(const A& r)> foo1 = [](const A& r) { 
//                                               ~~~~~~
    return r;
};

Note that there's no trailing return type. That means that it's automatically deduced. Deduction never gives you a reference type, so this lambda returns an A, not an A const&. That returned temporary A is then bound to the return A const& of function's operator(). That temporary is not lifetime-extended. But the time we finish calling foo1(), we have a dangling reference to that temporary A. This is undefined behavior, which I guess with your compiler, gave you a helpful runtime error.

To fix this, you need to explicitly specify the return type:

function<const A&(const A& r)> foo1 = [](const A& r) -> A const& { 
    return r;
};

But even this is dangerous, since you can still pass a temporary A into this function and get a dangling reference out. No real way around that one.


The ease of falling into this trap is also LWG Issue 2813

like image 67
Barry Avatar answered Oct 24 '22 01:10

Barry


While your function object returns const A& the lambda you provide it does not. It's return type is deduced from the return statement, which is deduced to be A. Try adding an explicit return type like this instead.

function<const A&(const A& r)> foo1 = [](const A& r) -> const A& { 
    return r;
};
like image 24
François Andrieux Avatar answered Oct 24 '22 01:10

François Andrieux