0

I found this piece of code at https://alexgaynor.net/2019/apr/21/modern-c++-wont-save-us/. The author says that

in main x goes out of scope, destroying the last reference to the data, and causing it to be freed. At this point y contains a dangling pointer.

But, actually, y() is already dangling inside that scope. With a gcc implementation I tried, it outputs

2
1
3

in line with that.

What is going on here and how would you summarize the reason for the undefined behaviour that is occurring?

My understanding is that the object corresponding to the labda holds a ref to the shared_ptr x that is captured, but the shared_ptr object itself is passed in by value, so thats gonna be a dangling reference to that because of the automatic lifetime of x (as an argument in f). So, when y() is invoked, the () operator is going to do some .operator*() on that x reference, is that correct? So, the dangling pointer has nothing to do with the refcount of that x shared_ptr inside main at the end of the day in that case and the author is simply wrong?

#include <memory>
#include <iostream>
#include <functional>

std::function<int(void)> f(std::shared_ptr<int> x) {
    return [&]() { return *x; };
}


int main() {
    std::function<int(void)> y(nullptr);
    {
        std::shared_ptr<int> x(std::make_shared<int>(1));
       
        y = f(x);
        
        std::shared_ptr<int> u(std::make_shared<int>(2));
        std::cout << y() << std::endl;
        std::cout << *x << std::endl;
    }

    std::shared_ptr<int> v(std::make_shared<int>(3));
    std::cout << y() << std::endl;
}
  • 4
    You capture `x` by reference. But the life-time of `x` ends when the function returns. Your lambda attempt to use the non-existing `x` object. So yes your thoughts seem correct. Capture `x` *by value* instead. – Some programmer dude May 18 '22 at 12:00
  • *"the dangling pointer has nothing to do with the refcount of that x shared_ptr inside main"* that is correct. – j6t May 18 '22 at 12:13
  • *"y() is **already** dangling inside that scope"* -- this does not accurately describe what you seem to mean based on your explanation. It is not `y()` that is dangling, but data contained in `y` (no parentheses since the object contains the data, no need for a function call). Perhaps a better phrasing: "it's a dangling reference, not a pointer, and it is **already** dangling inside that scope". – JaMiT May 18 '22 at 12:19
  • Hmm... looks like a typo in the article to me. Read two sentences beyond what you quoted: *"And yes, people really do write code that handles `std::shared_ptr&`"*. Yet, there is no `std::shared_ptr&` in the code. The author probably intended the parameter to `f` to be passed by reference, rather than by value. There is a "Contact" link on that site -- you could ask the author if it is a typo. – JaMiT May 18 '22 at 12:27
  • 1
    @JaMiT The reference is in the lambda capture `[&]`, not the `x` parameter to `f`. – Mankarse May 18 '22 at 12:35
  • 1
    This code is wrong for the reason you already figured out. – user253751 May 18 '22 at 12:41
  • 1
    @JaMiT Aaah, this could be a typo indeed! In that case, the things check out more or less, although the wording could be made more specific. – Attila Szasz May 18 '22 at 12:43
  • 1
    @Mankarse Yes, there is a reference in the lambda capture. If that capture was explicitly `&x`, then I might be inclined to think that is what the author was referring to. However, the capture is a generic `[&]` which does not appear to me to be intended as "an attempt to avoid additional increment and decrements on the reference count" (the rest of the sentence I quoted earlier). Plus, the author's text would be more correct if the parameter to `f` was passed by reference. So I am still inclined to think the author made a typo on the parameter. (You, of course, may see it differently.) – JaMiT May 18 '22 at 12:52
  • @JaMiT Good point – Mankarse May 18 '22 at 12:55

0 Answers0