#include <cstdio>
#include <cstdlib>
#include <utility>


template <typename T>
struct default_delete {
    void operator ()(T *ptr) {
        delete ptr;
    }
};

template <typename T, typename Deleter = default_delete<T>>
class unique_ptr {
public:
    unique_ptr(T* resource = nullptr, Deleter deleter = Deleter{})
        : resource{resource}, deleter{deleter} {}

    unique_ptr(const unique_ptr&) = delete;
    unique_ptr(unique_ptr&& other) 
        : resource{std::move(other.resource)},
          deleter{std::move(other.deleter)} {}

    unique_ptr& operator=(const unique_ptr&) = delete;
    unique_ptr& operator=(unique_ptr&& other) {
        using std::swap;
        swap(other.resource, resource);
        swap(other.deleter, deleter);
    }

    ~unique_ptr() {
        deleter(resource);
    }

    T* get() { return resource; }
    const T* get() const { return resource; }

private:
    T* resource;
    Deleter deleter;
};


/**** example starts here ****/

// partial specialization for arrays of default_delete
template <typename T>
struct default_delete<T[]> {
    void operator()(T arr[]) {
        delete[] arr;
    }
};

// partial specialization of uniqe_ptr for arrays
// (to avoid adding another pointer layer to the array)
template <typename T, typename Deleter>
class unique_ptr<T[], Deleter> {
public:
    unique_ptr(T resource[] = nullptr, Deleter deleter = Deleter{})
        : resource{resource}, deleter{deleter} {}

    unique_ptr(const unique_ptr&) = delete;
    unique_ptr(unique_ptr&& other) 
        : resource{std::move(other.resource)},
          deleter{std::move(other.deleter)} {}

    unique_ptr& operator=(const unique_ptr&) = delete;
    unique_ptr& operator=(unique_ptr&& other) {
        using std::swap;
        swap(other.resource, resource);
        swap(other.deleter, deleter);
    }

    ~unique_ptr() {
        deleter(resource);
    }

    T* get() { return resource; }
    const T* get() const { return resource; }

private:
    T* resource;
    Deleter deleter;
};


int main() {
    unique_ptr<int[]> arr_ptr(new int[5]); // uses the specialized version with
                                           // correct deleter
}
