Pointer handle - only allocates in stack This is a pointer class of mine that I would like to have reviewed. Right now it only allocates in the stack and has no copy or move operations.

template&lt;typename T&gt;
class Ptr {
public:
    Ptr(T* t, int s = 1) :p{t},sz{s} { }
    T&amp; operator*() {
        check_range();
        return *p;
    }
    T&amp; operator[](int i) {
        check_range(i);
        return p[i];
    }
    Ptr&amp; operator+(int i) {
        index += i;
        check_range(index);
        p+= i;
        return *this;
    }
    Ptr&amp; operator-(int i) {
        index -= i;
        check_range(index);
        p -= i;
        return *this;
    }
    Ptr&amp; operator++() {
        *this = *this+1;
        return *this;
    }
    Ptr operator++(int) {
        Ptr&lt;T&gt; old{p};
        ++(*this);
        return old;
    }
    Ptr&amp; operator--() {
        *this = *this-1;
        return *this;
    }
    Ptr operator--(int) {
        Ptr&lt;T&gt; old{p};
        --(*this);
        return old;
    }
private:
    T* p;
    int sz;
    int index = 0;
    void check_range(int i) {
        if (i &lt; 0 || i &gt; sz-1) {
            throw std::out_of_range("out of range");
        }
    }
    void check_range() {
        if (p == nullptr) {
            throw std::out_of_range("null pointer");
        }
    }
};


How can I make this shorter and less ugly? Is there a better solution than what I did?
I would expect operator+ and operator- to return a new object (leaving the current one intact.

    Ptr&amp; operator+(int i) {
        index += i;
        check_range(index);
        p+= i;
        return *this;
    }
    Ptr&amp; operator-(int i) {
        index -= i;
        check_range(index);
        p -= i;
        return *this;
    }


If I was using normal pointers.

 T* x  = getAT();
 x + 1;           // Does not change x
 T* y = x + 1;    // Does not change x (but y has the value of x+1).


Usually you see operator+ defined in terms of operator+=

Ptr&amp; operator+(int i) {
   Ptr result(*this);
   return result+=1;
}


The *this = *this+1; look very strange.

    Ptr&amp; operator++() {
        *this = *this+1;
        return *this;
    }


I would have just called the operator+=

    Ptr&amp; operator++() {
        return operator+=(1);
    }


Again don't like ++(*this);

    Ptr operator++(int) {
        Ptr&lt;T&gt; old{p};
        ++(*this);
        return old;
    }


I would have used:

    Ptr operator++(int) {
        Ptr&lt;T&gt; old{p};
        operator++();
        return old;
    }


You should strive for the strong exception guarantee (also known as transaction exception guarantee).

    void check_range(int i) {
        if (i &lt; 0 || i &gt; sz-1) {
            throw std::out_of_range("out of range");
        }
    }


Though this code looks like it does that. It is usually called in a way that prevents it.

    index -= i;
    check_range(index);  // If this throws your object is now in the new state
                         // with index out of range. I would have tried to make sure
                         // that if an exception is thrown then the state of the object
                         // remains unchanged.


    // Change to this:

    check_range(index - i); // If it throws then the object is still
                            // in a consistent unchanged state.
    index -= 1;             // If we get here then change the state.