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 <typename T>
explicit Holder(T const volatile* t):
_data(static_cast<void const volatile*>(t)),
_delete(&Delete<T>)
{}
void apply() { _deleter(_data); }
private:
typedef void (*Deleter)(void const volatile*);
template <typename T>
static void Delete(void const volatile* p) {
delete static_cast<T const volatile*>(p);
}
void const volatile* _data;
Deleter _deleter;
};
And now:
std::list<Holder> owningList;
owningList.push_back(Holder(somePointer));
for (Holder& h: owningList) { h.apply(); }