14

I have an inner lambda that uses one of the referenced variables of the outer lambda like this:

int x=0;
auto outer=[&](){
   return [&](){
        x=5;
    };
};

auto inner= outer();
inner();
std::cout << x;

I tried it. It worked well. However, I want to make sure that there is no dangling reference here. Is there?

Humam Helfawi
  • 19,566
  • 15
  • 85
  • 160
  • 8
    The way I see it - it's the reference to the same `x`, and since you didn't leave the scope where it was defined - it isn't a dangling reference. But, I'll let more knowledgeable people on SO answer it. – Algirdas Preidžius Apr 24 '17 at 09:13

3 Answers3

16

There is no dangling reference here. The reference of the inner lambda is not a reference to a reference (there is no such thing); it refers to x - which of course didn't go out of scope.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 4
    So, it is true there is no longer such a thing as "references to references", and never really was. But there was a defect in C++11 that tied the lifetime of reference captured variables in lambdas to the lifetime of the reference variable they captured, not to the lifetime of the thing they where referring to. There where even optimization related reasons why this might be a good idea (as it permitted `[&]` lambdas to capture a stack-frame pointer and optionally a `this` pointer and nothing else). – Yakk - Adam Nevraumont Apr 24 '17 at 13:57
  • @Yakk That's interesting. Thanks for pointing it out. I'm glad that my assumption is the *intention* of the standard. – eerorika Apr 24 '17 at 14:45
  • I now believe I am wrong. Read [the accepted answer here](https://stackoverflow.com/questions/21443023/capturing-a-reference-by-reference-in-a-c11-lambda?rq=1) – Yakk - Adam Nevraumont Apr 24 '17 at 14:50
  • @Yakk the answer seems similar to what you said, except according to it seems that the wording that breaks this code was added after C++14, and the that it should (hopefully?) be fixed before C++17 is released. – eerorika Apr 24 '17 at 15:15
  • Yes, it was convoluted but safe in C++11 and C++14. Then wording was added post C++14 and fixed prior to C++17 that led to the problem. The issue was that the wording to fix it wasn't at the same spot where the the rewording that caused the problem! So I (and the original answer to the link) was confused and thought it was a defect in C++14 being fixed; actually it was a C++17 defect being fixed prior to C++17 being finalized. – Yakk - Adam Nevraumont Apr 24 '17 at 17:08
7

As shown, you're calling the lambda within the block scope where x is declared, and there's no dangling reference.

It's worth noting that the inner anonymous lambda captures the reference to x directly from the outermost block scope rather than from the outer lambda, since it's looking for the declaration.

If you pass (a copy of) your lambda object outside that block scope, then you can cause a dangling reference.

Useless
  • 64,155
  • 6
  • 88
  • 132
4

If you rewrite the code without using lambdas then I think it is clear there is no dangling reference, simply a reference to a variable x that is still in scope:

class Inner {
    int& x;
  public:
    Inner(int &x) : x(x) {}
    void operator()(){
        x = 5;
    }
};

class Outer {
    int& x;
  public:
    Outer(int &x) : x(x) {}
    Inner operator()(){
        return {x};
    }
};

int main() {
    int x=0;
    auto outer = Outer{x};
    auto inner = outer();
    inner();
    std::cout << x;
}
Chris Drew
  • 14,926
  • 3
  • 34
  • 54