#include <memory>
#include <iostream>

template<typename T, typename TPtr>
struct Deep
{
	TPtr ptr;
	
	Deep() : ptr(new T) {}
	
	T get() const { return *ptr; }
	void set(T t) { *ptr = t; }
	
	Deep(const Deep& other): ptr(new T(*other.ptr)) {}
};

template<typename T, typename TPtr>
struct Shallow
{
	TPtr ptr;
	
	Shallow() : ptr(new T) {}
	
	T get() const { return *ptr; }
	void set(T t) { *ptr = t; }
	
	Shallow(const Shallow& other) = default;
};

template<typename T>
using raw_ptr = T*;

template<typename T>
void test(const T& a1)
{
	auto a2 = a1;
	a2.set(a2.get() + 1);
	std::cout << a1.get() << " " << a2.get() << std::endl;	
}

using std::shared_ptr;

int main()
{
	Deep<int, raw_ptr<int> > rawDeep;
	rawDeep.set(1);
	test(rawDeep);
	
	Deep<int, shared_ptr<int> > sharedDeep;
	sharedDeep.set(1);
	test(sharedDeep);
	
	Shallow<int, raw_ptr<int> > rawShallow;
	rawShallow.set(1);
	test(rawShallow);
	
	Shallow<int, shared_ptr<int> > sharedShallow;
	sharedShallow.set(1);
	test(sharedShallow);
}