/* This file is part of Fennix Kernel. Fennix Kernel is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fennix Kernel is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Fennix Kernel. If not, see . */ #pragma once #include #include #include #include #include #include #include #include #include namespace std { namespace detail { template constexpr bool is_unbounded_array_v = false; template constexpr bool is_unbounded_array_v = true; template constexpr bool is_bounded_array_v = false; template constexpr bool is_bounded_array_v = true; } template constexpr T *construct_at(T *p, Args &&...args) { return ::new (static_cast(p)) T(std::forward(args)...); } template constexpr void destroy_at(T *p) { p->~T(); } template T *addressof(T &arg) { return reinterpret_cast(&const_cast(reinterpret_cast(arg))); } template const T *addressof(const T &&) = delete; template NoThrowForwardIt uninitialized_copy_n(InputIt first, Size count, NoThrowForwardIt d_first) { using ValueType = typename std::iterator_traits::value_type; NoThrowForwardIt current = d_first; try { for (Size i = 0; i < count; ++i, (void)++current, ++first) { ::new (static_cast(std::addressof(*current))) ValueType(*first); } return current; } catch (...) { for (; d_first != current; ++d_first) { d_first->~ValueType(); } throw; } } template NoThrowForwardIt uninitialized_copy_n(ExecutionPolicy &&policy, ForwardIt first, Size count, NoThrowForwardIt d_first) { return uninitialized_copy_n(first, count, d_first); } template ForwardIt uninitialized_fill_n(ForwardIt first, Size count, const T &value) { using V = typename std::iterator_traits::value_type; ForwardIt current = first; try { for (; count > 0; ++current, (void)--count) ::new (static_cast(std::addressof(*current))) V(value); return current; } catch (...) { for (; first != current; ++first) first->~V(); throw; } } template ForwardIt uninitialized_fill_n(ExecutionPolicy &&policy, ForwardIt first, Size count, const T &value) { return uninitialized_fill_n(first, count, value); } template struct pointer_traits { using pointer = Ptr; using element_type = typename Ptr::element_type; using difference_type = typename Ptr::difference_type; template using rebind = typename Ptr::template rebind; static pointer pointer_to(element_type &r) noexcept { return Ptr::pointer_to(r); } }; template struct pointer_traits { using pointer = T *; using element_type = T; using difference_type = std::ptrdiff_t; template using rebind = U *; static pointer pointer_to(element_type &r) noexcept { return std::addressof(r); } }; template struct allocation_result { Pointer ptr; SizeType count; }; template struct allocator_traits { typedef Alloc allocator_type; typedef typename Alloc::value_type value_type; typedef typename Alloc::pointer pointer; typedef typename Alloc::const_pointer const_pointer; // typedef typename Alloc::void_pointer void_pointer; // typedef typename Alloc::const_void_pointer const_void_pointer; // typedef typename std::pointer_traits::rebind void_pointer; // typedef typename std::pointer_traits::rebind const_void_pointer; typedef typename Alloc::difference_type difference_type; typedef typename Alloc::size_type size_type; // typedef typename Alloc::propagate_on_container_copy_assignment propagate_on_container_copy_assignment; typedef typename std::false_type propagate_on_container_copy_assignment; typedef typename Alloc::propagate_on_container_move_assignment propagate_on_container_move_assignment; typedef typename std::false_type propagate_on_container_swap; typedef typename Alloc::is_always_equal is_always_equal; template using rebind_alloc = typename Alloc::template rebind::other; template using rebind_traits = allocator_traits>; [[nodiscard]] static constexpr pointer allocate(Alloc &a, size_type n) { return a.allocate(n); } // [[nodiscard]] static constexpr pointer allocate(Alloc &a, size_type n, const_void_pointer hint) // { // return a.allocate(n, hint); // } [[nodiscard]] static constexpr std::allocation_result allocate_at_least(Alloc &a, size_type n) { return a.allocate_at_least(n); } static constexpr void deallocate(Alloc &a, pointer p, size_type n) { a.deallocate(p, n); } template static constexpr void construct(Alloc &a, T *p, Args &&...args) { std::construct_at(p, std::forward(args)...); } template static constexpr void destroy(Alloc &a, T *p) { std::destroy_at(p); } static constexpr size_type max_size(const Alloc &a) { return a.max_size(); } static constexpr Alloc select_on_container_copy_construction(const Alloc &a) { return a; } }; template struct allocator { public: typedef T value_type; typedef T *pointer; typedef const T *const_pointer; typedef T &reference; typedef const T &const_reference; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; typedef std::true_type propagate_on_container_move_assignment; typedef std::true_type is_always_equal; template struct rebind { typedef allocator other; }; allocator() {} allocator(const allocator &other) {} template allocator(const allocator &other) {} ~allocator() {} pointer allocate(size_type n, const void *hint = 0) { return static_cast(::operator new(n * sizeof(T))); } std::allocation_result allocate_at_least(std::size_t n) { return {static_cast(::operator new(n * sizeof(T))), n}; } void deallocate(T *p, std::size_t n) { ::operator delete(p); } pointer address(reference x) const { return &x; } const_pointer address(const_reference x) const { return &x; } }; template struct default_delete { constexpr default_delete() noexcept = default; template constexpr default_delete(const default_delete &d) noexcept {} constexpr void operator()(T *ptr) const { delete ptr; } }; template struct default_delete { constexpr default_delete() noexcept = default; template constexpr default_delete(const default_delete &d) noexcept {} template constexpr void operator()(U *ptr) const { delete[] ptr; } }; template > class unique_ptr { public: using pointer = T *; // std::remove_reference::type::pointer; using element_type = T; using deleter_type = Deleter; private: pointer _ptr; public: #pragma region Constructors constexpr unique_ptr() noexcept : _ptr(nullptr) {} constexpr unique_ptr(std::nullptr_t) noexcept : _ptr(nullptr) {} constexpr explicit unique_ptr(pointer p) noexcept : _ptr(p) {} // constexpr unique_ptr(pointer p, /* TODO */ d1) noexcept : _ptr(p) {} // constexpr unique_ptr(pointer p, /* TODO */ d2) noexcept : _ptr(p) {} constexpr unique_ptr(unique_ptr &&u) noexcept : _ptr(u.release()) {} template constexpr unique_ptr(unique_ptr &&u) noexcept : _ptr(u.release()) {} unique_ptr(const unique_ptr &) = delete; ~unique_ptr() { if (_ptr == nullptr) return; Deleter d; d(_ptr); } constexpr unique_ptr &operator=(unique_ptr &&r) noexcept { reset(r.release()); return *this; } template constexpr unique_ptr &operator=(unique_ptr &&r) noexcept { reset(r.release()); return *this; } constexpr unique_ptr &operator=(std::nullptr_t) noexcept { reset(); return *this; } unique_ptr &operator=(const unique_ptr &) = delete; #pragma endregion Constructors #pragma region Modifiers constexpr pointer release() noexcept { pointer p = _ptr; _ptr = nullptr; return p; } constexpr void reset(pointer ptr = pointer()) noexcept { Deleter d; d(_ptr); _ptr = ptr; } void swap(unique_ptr &other) noexcept { pointer tmp = _ptr; _ptr = other._ptr; other._ptr = tmp; } #pragma endregion Modifiers #pragma region Observers constexpr pointer get() const noexcept { return _ptr; } constexpr Deleter &get_deleter() noexcept { return _ptr; } constexpr const Deleter &get_deleter() const noexcept { return _ptr; } constexpr explicit operator bool() const noexcept { return get() != nullptr; } #pragma endregion Observers #pragma region Element Access constexpr typename std::add_lvalue_reference::type operator*() const noexcept(noexcept(*std::declval())) { return *_ptr; } constexpr pointer operator->() const noexcept { return _ptr; } #pragma endregion Element Access }; template class unique_ptr { public: using pointer = T *; // std::remove_reference::type::pointer; using element_type = T; using deleter_type = Deleter; private: pointer _ptr; public: #pragma region Constructors constexpr unique_ptr() noexcept : _ptr(nullptr) {} constexpr unique_ptr(std::nullptr_t) noexcept : _ptr(nullptr) {} template constexpr explicit unique_ptr(U p) noexcept : _ptr(p) {} // template // constexpr unique_ptr(U p, /* TODO */ d1) noexcept : _ptr(p) {} // template // constexpr unique_ptr(U p, /* TODO */ d2) noexcept : _ptr(p) {} constexpr unique_ptr(unique_ptr &&u) noexcept : _ptr(u.release()) {} template constexpr unique_ptr(unique_ptr &&u) noexcept : _ptr(u.release()) {} unique_ptr(const unique_ptr &) = delete; ~unique_ptr() { if (_ptr == nullptr) return; Deleter d; d(_ptr); } constexpr unique_ptr &operator=(unique_ptr &&r) noexcept { reset(r.release()); return *this; } template constexpr unique_ptr &operator=(unique_ptr &&r) noexcept { reset(r.release()); return *this; } constexpr unique_ptr &operator=(std::nullptr_t) noexcept { reset(); return *this; } unique_ptr &operator=(const unique_ptr &) = delete; #pragma endregion Constructors #pragma region Modifiers constexpr pointer release() noexcept { pointer p = _ptr; _ptr = nullptr; return p; } template constexpr void reset(U ptr) noexcept { Deleter d; d(_ptr); _ptr = ptr; } constexpr void reset(std::nullptr_t = nullptr) noexcept { Deleter d; d(_ptr); _ptr = nullptr; } void swap(unique_ptr &other) noexcept { pointer tmp = _ptr; _ptr = other._ptr; other._ptr = tmp; } #pragma endregion Modifiers #pragma region Observers constexpr pointer get() const noexcept { return _ptr; } constexpr Deleter &get_deleter() noexcept { return _ptr; } constexpr const Deleter &get_deleter() const noexcept { return _ptr; } constexpr explicit operator bool() const noexcept { return get() != nullptr; } #pragma endregion Observers #pragma region Element Access constexpr T &operator[](std::size_t i) const { return _ptr[i]; } #pragma endregion Element Access }; template std::enable_if_t::value, std::unique_ptr> make_unique(Args &&...args) { return std::unique_ptr(new T(std::forward(args)...)); } template std::enable_if_t, std::unique_ptr> make_unique(std::size_t n) { return std::unique_ptr(new std::remove_extent_t[n]()); } template std::enable_if_t> make_unique(Args &&...) = delete; template requires(!std::is_array_v) std::unique_ptr make_unique_for_overwrite() { return std::unique_ptr(new T); } template requires std::is_unbounded_array_v std::unique_ptr make_unique_for_overwrite(std::size_t n) { return std::unique_ptr(new std::remove_extent_t[n]); } template requires std::is_bounded_array_v void make_unique_for_overwrite(Args &&...) = delete; template constexpr bool operator==(const unique_ptr &x, const unique_ptr &y) { return x.get() == y.get(); } template bool operator<(const unique_ptr &x, const unique_ptr &y) { return std::less::pointer, typename unique_ptr::pointer>::type>()(x.get(), y.get()); } template bool operator<=(const unique_ptr &x, const unique_ptr &y) { return !(y < x); } template bool operator>(const unique_ptr &x, const unique_ptr &y) { return y < x; } template bool operator>=(const unique_ptr &x, const unique_ptr &y) { return !(x < y); } // operator<=>(const unique_ptr &x, const unique_ptr &y); template constexpr bool operator==(const unique_ptr &x, std::nullptr_t) noexcept { return !x; } template constexpr bool operator<(const unique_ptr &x, std::nullptr_t) { return std::less::pointer>()(x.get(), nullptr); } template constexpr bool operator<(std::nullptr_t, const unique_ptr &y) { return std::less::pointer>()(nullptr, y.get()); } template constexpr bool operator<=(const unique_ptr &x, std::nullptr_t) { return !(nullptr < x); } template constexpr bool operator<=(std::nullptr_t, const unique_ptr &y) { return !(y < nullptr); } template constexpr bool operator>(const unique_ptr &x, std::nullptr_t) { return nullptr < x; } template constexpr bool operator>(std::nullptr_t, const unique_ptr &y) { return y < nullptr; } template constexpr bool operator>=(const unique_ptr &x, std::nullptr_t) { return !(x < nullptr); } template constexpr bool operator>=(std::nullptr_t, const unique_ptr &y) { return !(nullptr < y); } // operator<=>(const unique_ptr &x, std::nullptr_t); // template // std::basic_ostream &operator<<(std::basic_ostream &os, const std::unique_ptr &p) // { // return os << p.get(); // } template void swap(std::unique_ptr &lhs, std::unique_ptr &rhs) noexcept { lhs.swap(rhs); } template struct uses_allocator : std::integral_constant { }; template constexpr bool uses_allocator_v = uses_allocator::value; struct allocator_arg_t { explicit allocator_arg_t() = default; }; constexpr std::allocator_arg_t allocator_arg{}; template class weak_ptr; template class shared_ptr { private: struct control_block_base { long ref_count; virtual void destroy() = 0; virtual void *get_ptr() const = 0; control_block_base() : ref_count(1) {} virtual ~control_block_base() = default; }; template struct control_block_impl : control_block_base { Y *ptr; Deleter deleter; void destroy() override { deleter(ptr); } void *get_ptr() const override { return ptr; } control_block_impl(Y *p, Deleter d) : ptr(p), deleter(d) {} ~control_block_impl() override {} }; struct control_block_default : control_block_base { T *ptr; void destroy() override { delete ptr; } void *get_ptr() const override { return ptr; } control_block_default(T *p) : ptr(p) {} ~control_block_default() override {} }; template struct control_block_alloc : control_block_base { Y *ptr; Deleter deleter; Alloc alloc; void destroy() override { deleter(ptr); } void *get_ptr() const override { return ptr; } control_block_alloc(Y *p, Deleter d, Alloc a) : ptr(p), deleter(d), alloc(a) {} ~control_block_alloc() override {} void *operator new(std::size_t sz, Alloc &a) { using AllocTraits = std::allocator_traits; return AllocTraits::allocate(a, sz); } void operator delete(void *p, std::size_t sz) { ::operator delete(p); } }; control_block_base *cb; public: using weak_type = std::weak_ptr; using element_type = std::remove_extent_t; constexpr shared_ptr() noexcept : cb(nullptr) {} constexpr shared_ptr(std::nullptr_t) noexcept : cb(nullptr) {} template explicit shared_ptr(Y *ptr) : cb(ptr ? new control_block_default(ptr) : nullptr) {} template shared_ptr(Y *ptr, Deleter d) : cb(ptr ? new control_block_impl(ptr, d) : nullptr) {} template shared_ptr(std::nullptr_t ptr, Deleter d) : cb(nullptr) {} template shared_ptr(Y *ptr, Deleter d, Alloc alloc) : cb(ptr ? new(alloc) control_block_alloc(ptr, d, alloc) : nullptr) {} template shared_ptr(std::nullptr_t ptr, Deleter d, Alloc alloc) : cb(nullptr) {} template shared_ptr(const shared_ptr &r, element_type *ptr) noexcept : cb(r.cb) { if (cb) ++cb->ref_count; } template shared_ptr(shared_ptr &&r, element_type *ptr) noexcept : cb(r.cb) { r.cb = nullptr; } shared_ptr(const shared_ptr &r) noexcept : cb(r.cb) { if (cb) ++cb->ref_count; } template shared_ptr(const shared_ptr &r) noexcept : cb(r.cb) { if (cb) ++cb->ref_count; } shared_ptr(shared_ptr &&r) noexcept : cb(r.cb) { r.cb = nullptr; } template shared_ptr(shared_ptr &&r) noexcept : cb(r.cb) { r.cb = nullptr; } template explicit shared_ptr(const std::weak_ptr &r) : cb(nullptr) { /* Not implemented */ } template shared_ptr(std::unique_ptr &&r) : cb(r.get() ? new control_block_impl(r.release(), Deleter()) : nullptr) {} ~shared_ptr() { if (cb) { if (--cb->ref_count == 0) { cb->destroy(); delete cb; } } } shared_ptr &operator=(const shared_ptr &r) noexcept { if (this != &r) { if (cb && --cb->ref_count == 0) { cb->destroy(); delete cb; } cb = r.cb; if (cb) ++cb->ref_count; } return *this; } template shared_ptr &operator=(const shared_ptr &r) noexcept { if (cb && --cb->ref_count == 0) { cb->destroy(); delete cb; } cb = r.cb; if (cb) ++cb->ref_count; return *this; } shared_ptr &operator=(shared_ptr &&r) noexcept { if (this != &r) { if (cb && --cb->ref_count == 0) { cb->destroy(); delete cb; } cb = r.cb; r.cb = nullptr; } return *this; } template shared_ptr &operator=(shared_ptr &&r) noexcept { if (cb && --cb->ref_count == 0) { cb->destroy(); delete cb; } cb = r.cb; r.cb = nullptr; return *this; } template shared_ptr &operator=(std::unique_ptr &&r) { reset(r.get(), Deleter()); r.release(); return *this; } void reset() noexcept { if (cb && --cb->ref_count == 0) { cb->destroy(); delete cb; } cb = nullptr; } template void reset(Y *ptr) { reset(); if (ptr) cb = new control_block_default(ptr); } template void reset(Y *ptr, Deleter d) { reset(); if (ptr) cb = new control_block_impl(ptr, d); } template void reset(Y *ptr, Deleter d, Alloc alloc) { reset(); if (ptr) cb = new (alloc) control_block_alloc(ptr, d, alloc); } void swap(shared_ptr &r) noexcept { control_block_base *tmp = cb; cb = r.cb; r.cb = tmp; } element_type *get() const noexcept { return cb ? static_cast(cb->get_ptr()) : nullptr; } T &operator*() const noexcept { return *get(); } T *operator->() const noexcept { return get(); } element_type &operator[](std::ptrdiff_t idx) const { return get()[idx]; } long use_count() const noexcept { return cb ? cb->ref_count : 0; } explicit operator bool() const noexcept { return cb != nullptr; } template bool owner_before(const shared_ptr &other) const noexcept { return cb < other.cb; } template bool owner_before(const std::weak_ptr &other) const noexcept { return cb < other.cb; } }; template shared_ptr make_shared(Args &&...args) { return shared_ptr(new T(std::forward(args)...)); } template shared_ptr make_shared(std::size_t N) { return shared_ptr(new T[N]); } template shared_ptr make_shared() { return shared_ptr(new T); } template shared_ptr make_shared(std::size_t N, const std::remove_extent_t &u) { return shared_ptr(new T[N](u)); } template shared_ptr make_shared(const std::remove_extent_t &u) { return shared_ptr(new T(u)); } template shared_ptr make_shared_for_overwrite() { return shared_ptr(new T); } template shared_ptr make_shared_for_overwrite(std::size_t N) { return shared_ptr(new T[N]); } template shared_ptr allocate_shared(const Alloc &alloc, Args &&...args) { using AllocTraits = std::allocator_traits; using U = typename std::remove_extent::type; Alloc a = alloc; U *ptr = AllocTraits::allocate(a, 1); try { AllocTraits::construct(a, ptr, std::forward(args)...); return shared_ptr(ptr, [a](U *p) mutable { AllocTraits::destroy(a, p); AllocTraits::deallocate(a, p, 1); }, a); } catch (...) { AllocTraits::deallocate(a, ptr, 1); throw; } } template shared_ptr allocate_shared(const Alloc &alloc, std::size_t N) { using AllocTraits = std::allocator_traits; using U = typename std::remove_extent::type; Alloc a = alloc; U *ptr = AllocTraits::allocate(a, N); try { for (std::size_t i = 0; i < N; ++i) AllocTraits::construct(a, ptr + i); return shared_ptr(ptr, [a, N](U *p) mutable { for (std::size_t i = 0; i < N; ++i) AllocTraits::destroy(a, p + i); AllocTraits::deallocate(a, p, N); }, a); } catch (...) { AllocTraits::deallocate(a, ptr, N); throw; } } template shared_ptr allocate_shared(const Alloc &alloc) { return allocate_shared(alloc); } template shared_ptr allocate_shared(const Alloc &alloc, std::size_t N, const std::remove_extent_t &u) { using AllocTraits = std::allocator_traits; using U = typename std::remove_extent::type; Alloc a = alloc; U *ptr = AllocTraits::allocate(a, N); try { for (std::size_t i = 0; i < N; ++i) AllocTraits::construct(a, ptr + i, u); return shared_ptr(ptr, [a, N](U *p) mutable { for (std::size_t i = 0; i < N; ++i) AllocTraits::destroy(a, p + i); AllocTraits::deallocate(a, p, N); }, a); } catch (...) { AllocTraits::deallocate(a, ptr, N); throw; } } template shared_ptr allocate_shared(const Alloc &alloc, const std::remove_extent_t &u) { return allocate_shared(alloc, 1, u); } template shared_ptr allocate_shared_for_overwrite(const Alloc &alloc) { using AllocTraits = std::allocator_traits; using U = typename std::remove_extent::type; Alloc a = alloc; U *ptr = AllocTraits::allocate(a, 1); return shared_ptr(ptr, [a](U *p) mutable { AllocTraits::deallocate(a, p, 1); }, a); } template shared_ptr allocate_shared_for_overwrite(const Alloc &alloc, std::size_t N) { using AllocTraits = std::allocator_traits; using U = typename std::remove_extent::type; Alloc a = alloc; U *ptr = AllocTraits::allocate(a, N); return shared_ptr(ptr, [a, N](U *p) mutable { AllocTraits::deallocate(a, p, N); }, a); } template std::shared_ptr static_pointer_cast(const std::shared_ptr &r) noexcept { auto p = static_cast::element_type *>(r.get()); return std::shared_ptr{r, p}; } template std::shared_ptr static_pointer_cast(std::shared_ptr &&r) noexcept { auto p = static_cast::element_type *>(r.get()); return std::shared_ptr{r, p}; } template std::shared_ptr dynamic_pointer_cast(const std::shared_ptr &r) noexcept { if (auto p = dynamic_cast::element_type *>(r.get())) return std::shared_ptr{r, p}; else return std::shared_ptr{}; } template std::shared_ptr dynamic_pointer_cast(std::shared_ptr &&r) noexcept { if (auto p = dynamic_cast::element_type *>(r.get())) return std::shared_ptr{r, p}; else return std::shared_ptr{}; } template std::shared_ptr const_pointer_cast(const std::shared_ptr &r) noexcept { auto p = const_cast::element_type *>(r.get()); return std::shared_ptr{r, p}; } template std::shared_ptr const_pointer_cast(std::shared_ptr &&r) noexcept { auto p = const_cast::element_type *>(r.get()); return std::shared_ptr{r, p}; } template std::shared_ptr reinterpret_pointer_cast(const std::shared_ptr &r) noexcept { auto p = reinterpret_cast::element_type *>(r.get()); return std::shared_ptr{r, p}; } template std::shared_ptr reinterpret_pointer_cast(std::shared_ptr &&r) noexcept { auto p = reinterpret_cast::element_type *>(r.get()); return std::shared_ptr{r, p}; } template Deleter *get_deleter(const std::shared_ptr &p) noexcept { return p.cb ? &p.cb->deleter : nullptr; } template bool operator==(const std::shared_ptr &lhs, const std::shared_ptr &rhs) noexcept { return lhs.get() == rhs.get(); } template bool operator!=(const std::shared_ptr &lhs, const std::shared_ptr &rhs) noexcept { return !(lhs == rhs); } // template // bool operator<(const std::shared_ptr &lhs, const std::shared_ptr &rhs) noexcept { return std::less()(lhs.get(), rhs.get()); } template bool operator>(const std::shared_ptr &lhs, const std::shared_ptr &rhs) noexcept { return rhs < lhs; } template bool operator<=(const std::shared_ptr &lhs, const std::shared_ptr &rhs) noexcept { return !(rhs < lhs); } template bool operator>=(const std::shared_ptr &lhs, const std::shared_ptr &rhs) noexcept { return !(lhs < rhs); } // template // std::strong_ordering operator<=>(const std::shared_ptr &lhs, const std::shared_ptr &rhs) noexcept { return std::compare_three_way{}(x.get(), y.get()); } template bool operator==(const std::shared_ptr &lhs, std::nullptr_t) noexcept { return !lhs; } template bool operator==(std::nullptr_t, const std::shared_ptr &rhs) noexcept { return !rhs; } template bool operator!=(const std::shared_ptr &lhs, std::nullptr_t) noexcept { return (bool)lhs; } template bool operator!=(std::nullptr_t, const std::shared_ptr &rhs) noexcept { return (bool)rhs; } // template // bool operator<(const std::shared_ptr &lhs, std::nullptr_t) noexcept { return std::less::element_type *>()(lhs.get(), nullptr); } // template // bool operator<(std::nullptr_t, const std::shared_ptr &rhs) noexcept { return std::less::element_type *>()(nullptr, rhs.get()); } template bool operator>(const std::shared_ptr &lhs, std::nullptr_t) noexcept { return nullptr < lhs; } template bool operator>(std::nullptr_t, const std::shared_ptr &rhs) noexcept { return rhs < nullptr; } template bool operator<=(const std::shared_ptr &lhs, std::nullptr_t) noexcept { return !(nullptr < lhs); } template bool operator<=(std::nullptr_t, const std::shared_ptr &rhs) noexcept { return !(rhs < nullptr); } template bool operator>=(const std::shared_ptr &lhs, std::nullptr_t) noexcept { return !(lhs < nullptr); } template bool operator>=(std::nullptr_t, const std::shared_ptr &rhs) noexcept { return !(nullptr < rhs); } // template // std::strong_ordering operator<=>(const std::shared_ptr &lhs, std::nullptr_t) noexcept { return std::compare_three_way{}(x.get(), static_cast::element_type*>(nullptr)); } // template // std::basic_ostream &operator<<(std::basic_ostream &os, const std::shared_ptr &ptr) { return os << ptr.get(); } template void swap(std::shared_ptr &lhs, std::shared_ptr &rhs) noexcept { lhs.swap(rhs); } // template // struct std::atomic>; template struct hash>; }