#include <iostream>
#include <type_traits>
#include <experimental/optional>

template <class C>
using opt = std::experimental::optional<C>;

auto nullopt =  std::experimental::nullopt;

class Nothing {};

template <class T>
class Maybe
{
    opt<T> t;
    public:
    Maybe(const T& tt) : t(tt) {}
    Maybe(const Maybe& mt) : t(mt.t) {}
    Maybe(Nothing n) : t(nullopt) {}
    Maybe() : t(nullopt) {}

    template<class M, class Q = T>
    typename std::enable_if<std::is_class<T>::value, Maybe<M>>::type 
    fetch(M Q::*memptr)
    {
        if (t)
            return Maybe<M>((*t).*memptr);
        else
            return Maybe<M>(Nothing());
    }

    template<class M, class Q = T>
    typename std::enable_if<std::is_class<T>::value, Maybe<M>>::type 
    fetch(Maybe<M> Q::*memptr)
    {
        if (t)
            return Maybe<M>((*t).*memptr);
        else
            return Maybe<M>(Nothing());
    }

    operator bool() const { return bool(t); }
    T& operator*() { return *t; }
    T* operator->() { return &*t; }
    const T& operator*() const { return *t; }
    const T* operator->() const { return &*t; }

};


struct C
{
    int d;
};

struct B
{
    Maybe<C> c;
    B(C c) : c(c) {}
    B() : c() {}
};

struct A
{
    Maybe<B> b;
    A(B b) : b(b) {}
    A() : b() {}
};

void test(Maybe<A> a)
{
    Maybe<int> d = a.fetch(&A::b).fetch(&B::c).fetch(&C::d);
    if (!d)
        std::cout << "empty\n";
   else
        std::cout << *d << "\n";
};

int main()
{
    Maybe<A> empty1{Nothing{}};
    Maybe<A> empty2{A()};
    Maybe<A> empty3{A(B())};
    Maybe<A> full  (A(B(C{42})));

    test(empty1);
    test(empty2);
    test(empty3);
    test(full);
}
