Skip to content

Instantly share code, notes, and snippets.

@dwilliamson
Last active April 26, 2020 08:36
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dwilliamson/2cbdeb0a57a937dc44909f76fc3f0a4e to your computer and use it in GitHub Desktop.
Save dwilliamson/2cbdeb0a57a937dc44909f76fc3f0a4e to your computer and use it in GitHub Desktop.
Scoped (unique) "smart" pointer, deleting the pointer on destruction. Doesn't wrap the unique pointer in accessor functions, instead requiring explicit dereferencing and a const pointer that can't (shouldn't) be modified externally.
template <typename Type>
struct uptr
{
uptr() = default;
uptr(Type* ptr)
: ptr(ptr)
{
}
~uptr()
{
delete ptr;
}
// Take ownership of a raw pointer
uptr& operator = (Type* in_ptr)
{
assert(ptr == nullptr);
// Cast away constness for assignments. Not ideal but the language is limited, here.
const_cast<Type*&>(ptr) = in_ptr;
return *this;
}
// Non copyable
uptr(const uptr&) = delete;
uptr& operator = (const uptr&) = delete;
// Move constructable, for return values
uptr(uptr&& rhs)
: ptr(rhs.ptr)
{
}
// Disable move assignment, for now
uptr& operator = (uptr&&) = delete;
// Variable itself is const to discourage users from modifying it. It's public and not wrapped by
// method access to prevent a tonne of generated code in debug and pointless stepping in/out of
// functions when retrieving the pointer.
Type* const ptr = nullptr;
};
@garettbass
Copy link

garettbass commented Apr 21, 2020

template <typename Type>
struct uptr
{
    uptr(Type* ptr = nullptr) : ptr(ptr) {}

    ~uptr() { delete ptr; }

    uptr(uptr&& rhs) : ptr(rhs.ptr) {
        new(&rhs) uptr(nullptr);
    }
  
    uptr& operator=(uptr&& rhs) {
        if (this != &rhs) {
            this->~uptr();
            new(this) uptr(std::move(rhs));
        }
        return *this;
    }

    Type* const ptr = nullptr;
};

Omitted value assignment in favor of move assignment. Constructors are permitted to mutate const members, do placement new is used to reconstruct instead of const_cast. Safer because move constructor always invalidates the source to avoid double deletion in cases such as:

uptr<foo> a = new foo();
uptr<foo> b = std::move(a);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment