It is not necessary to use a smart pointer, it's just smart.
That being said, there are many other possibilities; the only thing to do is to store the type information along the actual object.
class Holder {
public:
template
explicit Holder(T const volatile* t):
_data(static_cast(t)),
_delete(&Delete)
{}
void apply() { _deleter(_data); }
private:
typedef void (*Deleter)(void const volatile*);
template
static void Delete(void const volatile* p) {
delete static_cast(p);
}
void const volatile* _data;
Deleter _deleter;
};
And now:
std::list owningList;
owningList.push_back(Holder(somePointer));
for (Holder& h: owningList) { h.apply(); }