std::weak_ptr::weak_ptr(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

πŸ’– std::weak_ptr::weak_ptr πŸ’–

Okay bestie, so this is the manual page for std::weak_ptr::weak_ptr(3) β€” straight out of the C++ Standard Libary (yes that's how it's spelled on the page, I'm not fixing it, I'm just reporting the facts ✨). It's the constructor page for C++'s little non-owning observer buddy, and honestly? It's giving "I see you but I don't own you" energy. Let me walk you through it, no cap. 🫢


✨ Synopsis ✨

So there are like, seven (yes SEVEN) constructor overloads, and they're all marked (since C++11) which is iconic of them. Here's the whole lineup, exactly as written:

constexpr weak_ptr() noexcept;                    (1) (since C++11)
weak_ptr( const weak_ptr& r ) noexcept;           (2) (since C++11)
template< class Y >                               (2) (since C++11)
weak_ptr( const weak_ptr<Y>& r ) noexcept;
template< class Y >                               (2) (since C++11)
weak_ptr( const std::shared_ptr<Y>& r ) noexcept;
weak_ptr( weak_ptr&& r ) noexcept;                (3) (since C++11)
template< class Y >                               (3) (since C++11)
weak_ptr( weak_ptr<Y>&& r ) noexcept;

And the whole vibe of the thing? "Constructs new weak_ptr that potentially shares an object with r." Periodt. πŸ’…


πŸŽ€ Description πŸŽ€

Okay so let's break down each numbered group, because they all do slightly different things and you gotta know which is which.

  1. Default constructor. Constructs an empty weak_ptr. That's it, that's the whole thing. She's giving "I'm baby, I hold nothing." 🍼
  2. Copy-ish constructors. Constructs a new weak_ptr which shares an object managed by r. And listen β€” if r manages no object, then *this manages no object too. They match. Fr fr.
Okay but here's the part where you gotta pay attention: the templated overloads don't participate in the overload resolution unless Y* is implicitly convertible to T*, OR Y is the type "array of N U" for some type U and some number N, and T is the type "array of unknown bound of (possibly cv-qualified) U" β€” and that last bit is (since C++17), bestie, don't forget. So like, the compiler vibe-checks your types before it even lets these overloads in the door. πŸ™ˆ
  1. Move constructors. Moves a weak_ptr instance from r into *this. After this, r is empty and r.use_count() == 0. She's been emptied out, okay? πŸ’€ And the templated overload here doesn't participate in the overload resolution unless Y* is implicitly convertible to T*.

🌸 Parameters 🌸

  • r β€” a std::shared_ptr or std::weak_ptr that will be viewed by this std::weak_ptr. That's the whole parameter list. One (1) parameter. She's simple like that. πŸŽ€

πŸ¦‹ Notes πŸ¦‹

Okay so here's a fun little fact that's actually important: because the default constructor is constexpr, static std::weak_ptrs are initialized as part of static non-local initialization, before any dynamic non-local initialization begins. And why do we care? Because this makes it safe to use a std::weak_ptr in a constructor of any static object. So like, no static-init-order-fiasco drama here. We love that for us. ✨


🌈 Example 🌈

The page literally says // Run this code, and I'm not gonna argue with the page. So run this code, bestie:

        #include <iostream>
        #include <memory>

        struct Foo {};

        int main()
        {
            std::weak_ptr<Foo> w_ptr;

            {
                auto ptr = std::make_shared<Foo>();
                w_ptr = ptr;
                std::cout << "w_ptr.use_count() inside scope: " <<
       w_ptr.use_count() << '\n';
            }

            std::cout << "w_ptr.use_count() out of scope: " <<
       w_ptr.use_count() << '\n';
            std::cout << "w_ptr.expired() out of scope: "
                      << std::boolalpha << w_ptr.expired() << '\n';
        }

πŸ’… Output πŸ’…

        w_ptr.use_count() inside scope: 1
        w_ptr.use_count() out of scope: 0
        w_ptr.expired() out of scope: true

See how the use_count() drops to 0 and expired() flips to true once the shared owner goes out of scope? That's the whole point of a weak_ptr β€” she watches but doesn't keep alive. Main character energy but make it non-owning. πŸ‘‘


⭐ Defect reports ⭐

So the following behavior-changing defect reports were applied retroactively to previously published C++ standards. Translation: they fixed stuff after the fact and we're telling you about it. πŸ”₯

  • LWG 2315 β€” Applied to C++11. The behavior as published was: "move semantic was not enabled for weak_ptr". The correct behavior: "enabled". So like, they enabled it. Slay. βœ…

🫢 See also 🫢

  • operator= β€” assigns the weak_ptr (public member function). That's your "hey bestie, take this instead" move. πŸ’–

And that's the whole page, bestie! This was std::weak_ptr::weak_ptr(3) from the C++ Standard Libary, brought to you by http://cppreference.com, dated 2024.06.10. Go forth and construct your weak pointers responsibly. I'm obsessed with you for reading all the way through. πŸ’œπŸ¦‹βœ¨

πŸ“¦ where this page comes from