unique_ptr basic implementation for single objects This is an implementation to simulate the basic functionality of unique_ptr.
This doesn't provide features like custom deleter and make_unique().

I would really appreciate any feedback to improve the below code, any other api's that I should be providing etc.

my_unique_ptr.h

#ifndef MY_UNIQUE_PTR_H_
#define MY_UNIQUE_PTR_H_

#include &lt;utility&gt;

namespace kapil {
  template &lt;typename T&gt;
  class unique_ptr final {
    private:
      T* ptr_;
      unique_ptr(const unique_ptr&amp;) = delete;               // Make unique_ptr non copy constructible
      unique_ptr&amp; operator = (const unique_ptr&amp;) = delete;  // Make unique_ptr non copy assignable

    public:
      explicit unique_ptr (T* ptr = nullptr) noexcept
        : ptr_{ptr} { }

      unique_ptr(unique_ptr&lt;T&gt;&amp;&amp; rval) noexcept               // Move constructor
        : ptr_{rval.ptr_} {
        rval.ptr_ = nullptr;
      }

      unique_ptr&amp; operator = (unique_ptr&amp;&amp; rhs) noexcept {   // Move assignment
        delete ptr_;
        ptr_ = rhs.ptr_;
        rhs.ptr_ = nullptr;
        return *this;
      }

      ~unique_ptr() noexcept {
        delete ptr_;
      }


      T* release() noexcept {
        T* old_ptr = ptr_;
        ptr_ = nullptr;
        return old_ptr;
      }

      void reset(T* ptr = nullptr) noexcept {
        delete ptr_;
        ptr_ = ptr;
      }

      void swap(unique_ptr&amp; rhs) noexcept {
        std::swap(ptr_, rhs.ptr_);
      }

      T* get() const noexcept {
        return ptr_;
      }

      explicit operator bool() const noexcept {
        return (ptr_ != nullptr);
      }

      T&amp; operator * () const {
        return *ptr_;
      }

      T* operator -&gt; () const noexcept {
        return ptr_;
      }

      friend bool operator == (const unique_ptr&amp; lhs, const unique_ptr&amp; rhs) {
        return lhs.get() == rhs.get();
      }

      friend bool operator != (const unique_ptr&amp; lhs, const unique_ptr&amp; rhs) {
        return !(lhs == rhs);
      }
  };

  template &lt;typename T&gt;
  void swap(unique_ptr&lt;T&gt;&amp; lhs, unique_ptr&lt;T&gt;&amp; rhs) {
    lhs.swap(rhs);
  }
} //kapil

#endif
Don't mark your class final without a good reason. It inhibits user-freedom.
The default-access for members of a class is already private.
Explicitly deleting copy-constructor and copy-assignment-operator is superfluous, as you define move-constructor and move-assignment, which already suppresses them. Still, some assert being explicit adds clarity.
I wonder why you didn't declare construction from T* to be constexpr...
Try to consistently use the injected class-name (unique_ptr), instead of sporadically naming the template-arguments (unique_ptr&lt;T&gt;).
You are missing implicit upcasting in the move-ctor and move-assignment-operator.

template &lt;class U, class = std::enable_if_t&lt;
    std::has_virtual_destructor&lt;T&gt;() &amp;&amp; std::is_convertible&lt;U*, T*&gt;()&gt;&gt;
unique_ptr(unique_ptr&lt;U&gt;&amp;&amp; other) noexcept
: ptr_(other.release())
{}

template &lt;class U&gt;
auto operator=(std::unique_ptr&lt;U&gt;&amp;&amp; other) noexcept
-&gt; decltype((*this = unique_ptr(other))) {
    return *this = unique_ptr(other);
}

Take a look at std::exchange(object, value) from &lt;utility&gt;. It allows you to simplify some of your code.
If you use move-and-swap, you could isolate freeing of the referee to the dtor. Having it at only one place ensures you always do it the same, and is a good first step for retrofitting custom deleters. Not to mention that it in many cases simplifies the implementation.
(ptr != nullptr) can be simplified to ptr. In contexts where you have to force the type, !!ptr.
Why are op==() and op!=() inline-friend-functions, but swap() isn't? That's inconsistent. It's especially puzzling as they are all written to use the public interface only.
There is exactly one place where you don't have a single empty line between two functions, but two. Yes, that's nothing big.