#include <iostream>
#include <memory>
#include <utility>
#include <type_traits>
using namespace std;

template <typename T>
class has_operator
{
    typedef char yes;
    typedef char no[2];
	template <typename I> static I identity(I);

	template<typename U,U> struct Check;
	template <typename C> static yes& test(Check<decltype(identity(&C::operator->)),&C::operator-> >*);
    template <typename C> static no& test(...);

public:
    enum { value = sizeof(test<T>(0)) == sizeof(yes) };
};


template<class T, bool hasOperator = has_operator<T>::value>
struct PtrOperator {
	T* p;
	PtrOperator(T*pp) : p(pp) {}
	T* operator->() const { return p;}
};

template<class T> 
struct PtrOperator<T,true>{
	T* p;
	PtrOperator<T,true>(T*pp) : p(pp) {}
	T& operator->() const { return *p;}
};

template<class T, class  Pref, class Suf> class  Wrap;
template<class T, class Suf>
class  Call_proxy : public PtrOperator<T>{
	mutable bool own;
	Suf suffix;
	Call_proxy(T* pp, Suf su) : PtrOperator<T>(pp) , own(true) , suffix(su) { }  // restrict creation
	Call_proxy& operator=(const  Call_proxy&) ;  // prevent assignment
public:
	template<class  U, class  P, class S> friend class  Wrap;
	Call_proxy(const  Call_proxy& a) : PtrOperator<T>(a.p) , own(true) , suffix(a.suffix) { a.own=false; }
	~Call_proxy() { if (own) suffix() ; }
};

template<class T, class  Pref, class Suf>
class  Wrap {
	T& p;
	Pref prefix;
	Suf suffix;
public:
	Wrap(T& x,  Pref pr, Suf su) :p(x) , prefix(pr) , suffix(su) { }
	Call_proxy<T,Suf> operator->() const{ prefix() ; return  Call_proxy<T,Suf>(&p,suffix) ; }
};

class X  { // one user class
public:
	X() { cout << " make an  X\n"; }
	~X() { cout << "destroy an  X\n"; }
	int f() const{ cout << "f()"; return 1; }
	void g() const{ cout << "g()"; }

	//X* operator->(){return this;}
};

void prefix() { cout << "prefix "; }
void suffix() { cout << " suffix\n"; }

struct  Pref { void operator()() const{ cout<< " Pref "; } };
struct Suf { void operator()() const{ cout<< " Suf "; } };

template<class T>
struct Shared : public  Wrap<T,Pref, Suf>
{
	using Wrap<T,Pref,Suf>::operator->;
	Shared(T& obj) :  Wrap<T,Pref, Suf>(obj,Pref() , Suf()) { }
};

template<class T>
struct Tracer : public  Wrap<T,void(*)() ,void(*)()> {
	using Wrap<T,void(*)() ,void(*)()>::operator->;
	Tracer(T& x) :  Wrap<T,void(*)() ,void(*)()>(x,::prefix,::suffix) { }
};



template<class T> struct Sealed {
	T&p;
	Sealed(T&x ) : p(x){}
	T* operator->() {return &p;}
};

int  main()  // test program
{
	X x;
	Sealed<X> xx(x);
	Shared<Sealed<X>> xxx(xx) ;
	Tracer<Shared<Sealed<X>>> xxxx(xxx);
	
	auto y = std::make_shared<X>();
	Shared<decltype(y)> yy(y);
	Tracer<decltype(yy)>yyy(yy);
	
	xx->g();
	xxx->g();
	xxxx->g();
	
	y->g();
	yy->g();
	yyy->g();

	std::cout << has_operator<X>::value << std:: endl;
	std::cout << has_operator<Shared<X>>::value << std:: endl;
	std::cout << has_operator<Tracer<Shared<X>>>::value << std:: endl;

	Shared<X> z(x) ;
	Tracer<Shared<X>> zz(z);

	x.g();
	z->g();
	zz->g();

	return 0;
}