#include <cassert>
#include <typeinfo>
#include <type_traits>

struct const_t
{
	typedef void const* ptr_type;
	template<class T> struct qualified { typedef const T type; };
};

struct nonconst_t : const_t
{
	typedef void * ptr_type;
	template<class T> struct qualified { typedef T type; };
};

template<class Constness = nonconst_t>
class anyptr
{
	typedef typename Constness::ptr_type ptr_type;
	ptr_type ptr;
	std::type_info const* ti;
	template<class> friend class anyptr;

public:
	anyptr()
	: ptr(0), ti(0)
	{}

	template<class C>
	anyptr(anyptr<C> const& p,
		typename std::enable_if<
			std::is_convertible<C,Constness>::value
		>::type* = 0)
	: ptr(p.ptr), ti(p.ti)
	{}

	template<class T>
	anyptr(T* p,
		typename std::enable_if<
			std::is_convertible<T*,ptr_type>::value
		>::type* =0)
	: ptr(p), ti(&typeid(typename std::remove_const<T>::type))
	{}

	explicit operator bool() const
	{ return ptr!=0; }

	std::type_info const& static_type() const
	{ assert(ti!=0); return *ti; }

	template<class T>
	bool static_type_match() const
	{ return ti && *ti==typeid(T); }

	template<class T>
	typename Constness::template qualified<T>::type*
	get() const {
		return static_type_match<T>() ? static_cast<
			typename Constness::template qualified<T>::type*
		>(ptr) : 0;
	}

	template<class T>
	typename Constness::template qualified<T>::type&
	deref() const {
		if (!static_type_match<T>()) throw std::bad_cast();
		return *static_cast<
			typename Constness::template qualified<T>::type*
		>(ptr);
	}
};

#include <iostream>
using std::cout;
using std::endl;

void check(anyptr<const_t> p)
{
	if (!p) {
		cout << "p zeigt auf gar nix." << endl;
	} else
	if (p.static_type_match<int>()) {
		cout << "p zeigt auf einen int mit dem Wert " << p.deref<int>() << endl;
	} else
	if (p.static_type_match<double>()) {
		cout << "p zeigt auf einen double mit dem Wert " << p.deref<double>() << endl;
	} else {
		cout << "p zeigt auf ein Objekt des Typs " << p.static_type().name() << endl;
	}
}

int main()
{
	try {
		int i = 23;
		double d = 3.1416;
		check(&i);
		check(&d);
		anyptr<> q = &i;
		anyptr<const_t> qc = q; // klappt die konvertierung?
		cout << "Versuche einen int-Zeiger als double-Zeiger zu dereferenzieren..." << endl;
		cout << qc.deref<double>() + 1234.5 << endl;
	} catch (std::bad_cast const& x) {
		cout << "Upps! Geht doch nicht!" << endl;
	}
}
