#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);
}