Pointer handle - absolute follow-up
Ptr(T* t, int s = 1): sz{s&lt;1 ? throw std::logic_error("Invalid sz parameter") : s}
{
    sz = s; //this is unnecessary
    p = new T[sz];
    std::copy(t,t+sz,p);
}


Here you are writing to sz twice, first in the initializer list then in the constructors body. In general it's best if you just write only once in the initializer list if possible:

Ptr(T* t, int s = 1):
    sz{s&lt;1 ? throw std::logic_error("Invalid sz parameter") : s}
{
    p = new T[sz];
    std::copy(t,t+sz,p);
}


In the move constructor:

Ptr(Ptr &amp;&amp;t):
    p{t.p},
    sz{t.sz}
{
    t.p = nullptr;
    t.sz = 0;
}


we are changing the pointer and size in this class but not the index. I would "move" everything and take the index from the other class here as well.
We need to set t.p to nullptr so when t gets destructed the the call to delete[] p; will be a no-op, so that part is necessary. However I don't see why the sz needs to be set to 0 because it's a primitive and therefore not in the destructor. Because the range checking function always checks p for a nullptr we can't index the old object anyway so I think I would just remove that expression.

Ptr(Ptr &amp;&amp;t):
    p{t.p},
    sz{t.sz},
    index{t.index}
{
    t.p = nullptr;
}


(Disclaimer: I'm not especially experienced with c++ move semantics so if you think it's more readable to keep explicitly setting every element to a "null" type of value for readability please make a comment, I'd like to know what people think about that.)