#pragma once #include #include #include #include namespace erebos { using std::function; using std::make_shared; using std::make_unique; using std::move; using std::shared_ptr; using std::unique_ptr; using std::variant; template class List { public: struct Nil { bool operator==(const Nil &) const { return true; } }; struct Cons { T head; List tail; bool operator==(const Cons & x) const { return head == x.head && tail == x.tail; } }; List(); List(const T head, List tail); const T & front() const; const List & tail() const; bool empty() const; bool operator==(const List &) const; bool operator!=(const List &) const; List push_front(T x) const; private: struct Priv; shared_ptr p; }; template struct List::Priv { variant value; function eval = {}; mutable std::once_flag once = {}; }; template List::List(): p(shared_ptr(new Priv { Nil() })) { std::call_once(p->once, [](){}); } template List::List(T head, List tail): p(shared_ptr(new Priv { Cons { move(head), move(tail) } })) { std::call_once(p->once, [](){}); } template const T & List::front() const { std::call_once(p->once, p->eval); return std::get(p->value).head; } template const List & List::tail() const { std::call_once(p->once, p->eval); return std::get(p->value).tail; } template bool List::empty() const { std::call_once(p->once, p->eval); return std::holds_alternative(p->value); } template bool List::operator==(const List & other) const { if (p == other.p) return true; std::call_once(p->once, p->eval); std::call_once(other.p->once, other.p->eval); return p->value == other.p->value; } template bool List::operator!=(const List & other) const { return !(*this == other); } template List List::push_front(T x) const { return List(move(x), *this); } }