ctl::unique_ptr improvements and cleanup (#1221)

Explicitly value-initializes the deleter, even though I have not found a
way to get the deleter to act like it’s been default-initialized in unit
tests so far.

Uses auto in reset. The static cast is apparently not needed (unless I’m
missing some case I didn’t think of.)

Implements the general move constructor - turns out that the reason this
didn’t work before was that default_delete<U> was not move constructible
from default_delete<T>.

Drop inline specifiers from functions defined entirely inside the struct
definition since they are implicitly inline.

* Cleans up reset to match spec

Remove the variants from the T[] specialization. Also follow the spec on
the order of operations in reset, which may matter if we are deleting an
object that has a reference to the unique_ptr that is being reset. (?)

* Tests Base/Derived reset.

* Adds some constexpr declarations.

* Adds default_delete specialization for T[].

* Makes parameters const.
This commit is contained in:
Steven Dee (Jōshin) 2024-06-20 18:44:31 -04:00 committed by GitHub
parent f86e6f8eb0
commit d7b1919b29
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 62 additions and 38 deletions

View file

@ -3,18 +3,38 @@
#ifndef COSMOPOLITAN_CTL_UNIQUE_PTR_H_ #ifndef COSMOPOLITAN_CTL_UNIQUE_PTR_H_
#define COSMOPOLITAN_CTL_UNIQUE_PTR_H_ #define COSMOPOLITAN_CTL_UNIQUE_PTR_H_
#include "utility.h" #include "utility.h"
#include <__type_traits/is_convertible.h>
namespace ctl { namespace ctl {
template<typename T> template<typename T>
struct default_delete struct default_delete
{ {
constexpr void operator()(T* p) const noexcept constexpr default_delete() noexcept = default;
template<typename U>
constexpr default_delete(default_delete<U>&&) noexcept
{
}
constexpr void operator()(T* const p) const noexcept
{ {
delete p; delete p;
} }
}; };
template<typename T>
struct default_delete<T[]>
{
constexpr default_delete() noexcept = default;
template<typename U>
constexpr default_delete(default_delete<U>&&) noexcept
{
}
constexpr void operator()(T* const p) const noexcept
{
delete[] p;
}
};
template<typename T, typename D = default_delete<T>> template<typename T, typename D = default_delete<T>>
struct unique_ptr struct unique_ptr
{ {
@ -25,102 +45,91 @@ struct unique_ptr
pointer p; pointer p;
[[no_unique_address]] deleter_type d; [[no_unique_address]] deleter_type d;
constexpr unique_ptr(nullptr_t = nullptr) noexcept : p(nullptr) constexpr unique_ptr(const nullptr_t = nullptr) noexcept : p(nullptr), d()
{ {
} }
constexpr unique_ptr(pointer p) noexcept : p(p) constexpr unique_ptr(const pointer p) noexcept : p(p), d()
{ {
} }
constexpr unique_ptr(pointer p, auto&& d) noexcept constexpr unique_ptr(const pointer p, auto&& d) noexcept
: p(p), d(ctl::forward<decltype(d)>(d)) : p(p), d(ctl::forward<decltype(d)>(d))
{ {
} }
constexpr unique_ptr(unique_ptr&& u) noexcept : p(u.p), d(ctl::move(u.d)) template<typename U, typename E>
requires std::is_convertible_v<U, T> && std::is_convertible_v<E, D>
constexpr unique_ptr(unique_ptr<U, E>&& u) noexcept
: p(u.p), d(ctl::move(u.d))
{ {
u.p = nullptr; u.p = nullptr;
} }
// TODO(mrdomino):
// template <typename U, typename E>
// unique_ptr(unique_ptr<U, E>&& u) noexcept;
unique_ptr(const unique_ptr&) = delete; unique_ptr(const unique_ptr&) = delete;
inline ~unique_ptr() /* noexcept */ constexpr ~unique_ptr() /* noexcept */
{ {
reset(); if (p)
d(p);
} }
inline unique_ptr& operator=(unique_ptr r) noexcept constexpr unique_ptr& operator=(unique_ptr r) noexcept
{ {
swap(r); swap(r);
return *this; return *this;
} }
inline pointer release() noexcept constexpr pointer release() noexcept
{ {
pointer r = p; pointer r = p;
p = nullptr; p = nullptr;
return r; return r;
} }
inline void reset(nullptr_t = nullptr) noexcept constexpr void reset(const pointer p2 = pointer()) noexcept
{ {
if (p) const pointer r = p;
d(p); p = p2;
p = nullptr; if (r)
d(r);
} }
template<typename U> constexpr void swap(unique_ptr& r) noexcept
// TODO(mrdomino):
/* requires is_convertible_v<U, T> */
inline void reset(U* p2)
{
if (p) {
d(p);
}
p = static_cast<pointer>(p2);
}
inline void swap(unique_ptr& r) noexcept
{ {
using ctl::swap; using ctl::swap;
swap(p, r.p); swap(p, r.p);
swap(d, r.d); swap(d, r.d);
} }
inline pointer get() const noexcept constexpr pointer get() const noexcept
{ {
return p; return p;
} }
inline deleter_type& get_deleter() noexcept constexpr deleter_type& get_deleter() noexcept
{ {
return d; return d;
} }
inline const deleter_type& get_deleter() const noexcept constexpr const deleter_type& get_deleter() const noexcept
{ {
return d; return d;
} }
inline explicit operator bool() const noexcept constexpr explicit operator bool() const noexcept
{ {
return p; return p;
} }
inline element_type& operator*() const element_type& operator*() const noexcept(noexcept(*ctl::declval<pointer>()))
noexcept(noexcept(*ctl::declval<pointer>()))
{ {
if (!p) if (!p)
__builtin_trap(); __builtin_trap();
return *p; return *p;
} }
inline pointer operator->() const noexcept pointer operator->() const noexcept
{ {
if (!p) if (!p)
__builtin_trap(); __builtin_trap();
@ -129,14 +138,14 @@ struct unique_ptr
}; };
template<typename T, typename... Args> template<typename T, typename... Args>
inline unique_ptr<T> constexpr unique_ptr<T>
make_unique(Args&&... args) make_unique(Args&&... args)
{ {
return unique_ptr<T>(new T(ctl::forward<Args>(args)...)); return unique_ptr<T>(new T(ctl::forward<Args>(args)...));
} }
template<typename T> template<typename T>
inline unique_ptr<T> constexpr unique_ptr<T>
make_unique_for_overwrite() make_unique_for_overwrite()
{ {
#if 0 #if 0

View file

@ -91,6 +91,12 @@ struct SetsGDtor
} }
}; };
struct Base
{};
struct Derived : Base
{};
int int
main() main()
{ {
@ -211,7 +217,16 @@ main()
Ptr<int, StatefulDeleter> y(&a); Ptr<int, StatefulDeleter> y(&a);
} }
{
Ptr<Base> x(new Base);
x.reset(new Derived);
Ptr<Derived> y(new Derived);
Ptr<Base> z(ctl::move(y));
}
// next is 18 // next is 18
CheckForMemoryLeaks();
return 0; return 0;
} }