diff options
| author | Lukas Oesch <lukasoesch20@gmail.com> | 2026-03-17 11:48:40 +0100 |
|---|---|---|
| committer | Lukas Oesch <lukasoesch20@gmail.com> | 2026-03-17 16:44:34 +0100 |
| commit | 471888c64ed490b1f1dbaa2c2f67a1e8d315905a (patch) | |
| tree | 0d41a93c517afe7cd4228ef44c1f34ec9a1e484c /libs | |
| parent | 9391c5881a8a635677312df80888e972d7175cfa (diff) | |
| download | teachos-471888c64ed490b1f1dbaa2c2f67a1e8d315905a.tar.xz teachos-471888c64ed490b1f1dbaa2c2f67a1e8d315905a.zip | |
extend shared_ptr to support nullptr and cross-type conversions
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/kstd/include/kstd/bits/shared_ptr.hpp | 131 |
1 files changed, 125 insertions, 6 deletions
diff --git a/libs/kstd/include/kstd/bits/shared_ptr.hpp b/libs/kstd/include/kstd/bits/shared_ptr.hpp index cfe5d18..ed23d29 100644 --- a/libs/kstd/include/kstd/bits/shared_ptr.hpp +++ b/libs/kstd/include/kstd/bits/shared_ptr.hpp @@ -3,12 +3,16 @@ #include <atomic> #include <cstddef> +#include <type_traits> #include <utility> // IWYU pragma: private, include <kstd/memory> namespace kstd { + template<typename T> + struct shared_ptr; + /** * @brief Shared_pointer is a smart pointer that retains shared ownership of an object through a pointer. Several * shared_ptr objects may own the same object. The object is destroyed and its memory deallocated when either of @@ -24,6 +28,17 @@ namespace kstd template<typename T> struct shared_ptr { + template<typename U> + friend struct shared_ptr; + + /** + * @brief Construct an empty shared_ptr from nullptr. + */ + shared_ptr(std::nullptr_t) noexcept + : pointer(nullptr) + , ref_count(nullptr) + {} + /** * @brief Constructor. * @@ -31,7 +46,7 @@ namespace kstd */ explicit shared_ptr(T * pointer = nullptr) : pointer(pointer) - , ref_count(new std::atomic<std::size_t>(pointer != nullptr ? 1 : 0)) + , ref_count(pointer != nullptr ? new std::atomic<std::size_t>(1) : nullptr) { // Nothing to do. } @@ -45,7 +60,25 @@ namespace kstd : pointer(other.pointer) , ref_count(other.ref_count) { - if (pointer != nullptr) + if (ref_count != nullptr) + { + ++(*ref_count); + } + } + + /** + * @brief Converting copy constructor for compatible shared_ptr types. + * + * @tparam U Source pointer element type. + * @param other The shared_ptr to copy from. + */ + template<typename U> + requires(std::is_convertible_v<U *, T *>) + shared_ptr(shared_ptr<U> const & other) + : pointer(other.pointer) + , ref_count(other.ref_count) + { + if (ref_count != nullptr) { ++(*ref_count); } @@ -65,6 +98,22 @@ namespace kstd } /** + * @brief Converting move constructor for compatible shared_ptr types. + * + * @tparam U Source pointer element type. + * @param other The shared_ptr to move from. + */ + template<typename U> + requires(std::is_convertible_v<U *, T *>) + shared_ptr(shared_ptr<U> && other) noexcept + : pointer(other.pointer) + , ref_count(other.ref_count) + { + other.pointer = nullptr; + other.ref_count = nullptr; + } + + /** * @brief Copy assignment operator. Replaces the managed object with the one managed by r. Shares ownership of the * object managed by r. If r manages no object, *this manages no object too. Equivalent to * shared_ptr<T>(r).swap(*this). @@ -80,7 +129,7 @@ namespace kstd pointer = other.pointer; ref_count = other.ref_count; - if (pointer != nullptr) + if (ref_count != nullptr) { ++(*ref_count); } @@ -90,6 +139,29 @@ namespace kstd } /** + * @brief Converting copy assignment for compatible shared_ptr types. + * + * @tparam U Source pointer element type. + * @param other Another smart pointer to share ownership with. + * @return Reference to this shared pointer. + */ + template<typename U> + requires(std::is_convertible_v<U *, T *>) + auto operator=(shared_ptr<U> const & other) -> shared_ptr & + { + cleanup(); + pointer = other.pointer; + ref_count = other.ref_count; + + if (ref_count != nullptr) + { + ++(*ref_count); + } + + return *this; + } + + /** * @brief Move assignment operator. Move-assigns a shared_ptr from r. After the assignment, *this contains a copy of * the previous state of r, and r is empty. Equivalent to shared_ptr<T>(std::move(r)).swap(*this). * @@ -111,6 +183,37 @@ namespace kstd } /** + * @brief Converting move assignment for compatible shared_ptr types. + * + * @tparam U Source pointer element type. + * @param other Another smart pointer to acquire ownership from. + * @return Reference to this shared pointer. + */ + template<typename U> + requires(std::is_convertible_v<U *, T *>) + auto operator=(shared_ptr<U> && other) noexcept -> shared_ptr & + { + cleanup(); + pointer = other.pointer; + ref_count = other.ref_count; + other.pointer = nullptr; + other.ref_count = nullptr; + + return *this; + } + + /** + * @brief Reset this shared_ptr to empty via nullptr assignment. + */ + auto operator=(std::nullptr_t) noexcept -> shared_ptr & + { + cleanup(); + pointer = nullptr; + ref_count = nullptr; + return *this; + } + + /** * @brief Destructor. Cleans up resources if necessary. */ ~shared_ptr() @@ -127,7 +230,7 @@ namespace kstd { cleanup(); pointer = ptr; - ref_count = new std::atomic<std::size_t>(ptr != nullptr ? 1 : 0); + ref_count = ptr != nullptr ? new std::atomic<std::size_t>(1) : nullptr; } /** @@ -185,7 +288,7 @@ namespace kstd */ [[nodiscard]] auto use_count() const -> std::size_t { - if (pointer != nullptr) + if (ref_count != nullptr) { return *ref_count; } @@ -204,6 +307,22 @@ namespace kstd } /** + * @brief Compare shared_ptr with nullptr. + */ + [[nodiscard]] auto operator==(std::nullptr_t) const -> bool + { + return pointer == nullptr; + } + + /** + * @brief Compare nullptr with shared_ptr. + */ + friend auto operator==(std::nullptr_t, shared_ptr const & ptr) -> bool + { + return ptr.pointer == nullptr; + } + + /** * @brief Defaulted three-way comparator operator. */ auto operator<=>(shared_ptr const & other) const = default; @@ -214,7 +333,7 @@ namespace kstd */ auto cleanup() -> void { - if (pointer != nullptr && ref_count != nullptr && --(*ref_count) == 0) + if (ref_count != nullptr && --(*ref_count) == 0) { delete pointer; delete ref_count; |
