diff --git a/src/event_queue.hpp b/src/event_queue.hpp
index 0a8b33707a2d82e949717fd243479ce33776120e..989e34873a1f3ff851d0a3a9f85276cea9d9bf7a 100644
--- a/src/event_queue.hpp
+++ b/src/event_queue.hpp
@@ -1,9 +1,10 @@
 #pragma once
 
+#include <cstdint>
 #include <ostream>
 #include <queue>
 
-#include <cstdint>
+#include "util/optional.hpp"
 
 namespace nest {
 namespace mc {
@@ -45,14 +46,14 @@ public :
     }
 
     // pop until
-    std::pair<bool, local_event> pop_if_before(float t_until) {
+    util::optional<local_event> pop_if_before(float t_until) {
          if (!queue_.empty() && queue_.top().time < t_until) {
              auto ev = queue_.top();
              queue_.pop();
-             return {true, ev};
+             return ev;
          }
          else {
-             return {false, {}};
+             return util::nothing;
          }
     }
 
diff --git a/src/fvm.hpp b/src/fvm.hpp
index 4a908bba49887948e37bfa7fe3409b19df90bbb3..603196c0cc66ebb2293c09dfd9a153fd7a806267 100644
--- a/src/fvm.hpp
+++ b/src/fvm.hpp
@@ -19,7 +19,6 @@
 #include <util.hpp>
 
 #include <vector/include/Vector.hpp>
-
 #include <mechanisms/expsyn.hpp>
 
 namespace nest {
@@ -510,18 +509,14 @@ void fvm_cell<T, I>::advance_to(T tfinal, T dt)
     }
 
     do {
-        auto tnext = std::min(tfinal, t_+dt);
-        auto next = events_.pop_if_before(tnext);
-        // if there is an event before tnext...
-        if(next.first) {
-            tnext = next.second.time;
-        }
+        auto tstep = std::min(tfinal, t_+dt);
+        auto next = events_.pop_if_before(tstep);
+        auto tnext = next? next->time: tstep;
+
         advance(tnext-t_);
         t_ = tnext;
-        if(next.first) { // handle event
-            auto &e = next.second;
-
-            mechanisms_[synapse_index_]->net_receive(e.target, e.weight);
+        if (next) { // handle event
+            mechanisms_[synapse_index_]->net_receive(next->target, next->weight);
         }
     } while(t_<tfinal);
 }
diff --git a/src/util/optional.hpp b/src/util/optional.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f1fb805bfb86062f1c7c603befd5f01d54fad5f2
--- /dev/null
+++ b/src/util/optional.hpp
@@ -0,0 +1,310 @@
+#pragma once
+
+/*! \file optional.h
+ *  \brief An option class with a monadic interface.
+ *
+ *  The std::option<T> class was proposed for inclusion into C++14, but was
+ *  ultimately rejected. (See N3672 proposal for details.) This class offers
+ *  similar functionality, namely a class that can represent a value (or
+ *  reference), or nothing at all.
+ *
+ *  In addition, this class offers monadic and monoidal bindings, allowing
+ *  the chaining of operations any one of which might represent failure with
+ *  an unset optional value.
+ *
+ *  One point of difference between the proposal N3672 and this implementation
+ *  is the lack of constexpr versions of the methods and constructors.
+ */
+
+#include <type_traits>
+#include <stdexcept>
+#include <utility>
+
+#include "util/uninitialized.hpp"
+
+namespace nest {
+namespace mc {
+namespace util {
+
+template <typename X> struct optional;
+
+struct optional_unset_error: std::runtime_error {
+    explicit optional_unset_error(const std::string &what_str): std::runtime_error(what_str) {}
+    optional_unset_error(): std::runtime_error("optional value unset") {}
+};
+
+struct optional_invalid_dereference: std::runtime_error {
+    explicit optional_invalid_dereference(const std::string &what_str): std::runtime_error(what_str) {}
+    optional_invalid_dereference(): std::runtime_error("derefernce of optional<void> value") {}
+};
+
+struct nothing_t {};
+constexpr nothing_t nothing{};
+
+namespace detail {
+    template <typename Y> struct lift_type { using type=optional<Y>; };
+    template <typename Y> struct lift_type<optional<Y>> { using type=optional<Y>; };
+
+    struct optional_tag {};
+
+    template <typename X> struct is_optional {
+        enum {value=std::is_base_of<optional_tag,typename std::decay<X>::type>::value };
+    };
+
+    template <typename D,typename X> struct wrapped_type_ { using type=X; };
+    template <typename D,typename X> struct wrapped_type_<optional<D>,X> { using type=D; };
+
+    template <typename X> struct wrapped_type { using type=typename wrapped_type_<typename std::decay<X>::type,X>::type; };
+
+    template <typename X>
+    struct optional_base: detail::optional_tag {
+        template <typename Y> friend struct optional;
+
+    protected:
+        using D=util::uninitialized<X>;
+
+    public:
+        using reference_type=typename D::reference_type;
+        using const_reference_type=typename D::const_reference_type;
+        using pointer_type=typename D::pointer_type;
+        using const_pointer_type=typename D::const_pointer_type;
+
+    protected:
+        bool set;
+        D data;
+
+        optional_base(): set(false) {}
+
+        template <typename T>
+        optional_base(bool set_,T&& init): set(set_) { if (set) data.construct(std::forward<T>(init)); }
+
+        reference_type ref() { return data.ref(); }
+        const_reference_type ref() const { return data.cref(); }
+
+    public:
+        ~optional_base() { if (set) data.destruct(); }
+
+        const_pointer_type operator->() const { return data.ptr(); }
+        pointer_type operator->() { return data.ptr(); }
+            
+        const_reference_type operator*() const { return ref(); }
+        reference_type operator*() { return ref(); }
+            
+        reference_type get() {
+            if (set) return ref();
+            else throw optional_unset_error();
+        }
+
+        const_reference_type get() const {
+            if (set) return ref();
+            else throw optional_unset_error();
+        }
+
+        explicit operator bool() const { return set; }
+
+        template <typename Y>
+        bool operator==(const Y &y) const { return set && ref()==y; }
+
+        template <typename Y>
+        bool operator==(const optional<Y> &o) const {
+            return set && o.set && ref()==o.ref() || !set && !o.set;
+        }
+
+        void reset() {
+            if (set) data.destruct();
+            set=false;
+        }
+
+        template <typename F>
+        auto bind(F &&f) -> typename lift_type<decltype(data.apply(std::forward<F>(f)))>::type {
+            using F_result_type=decltype(data.apply(std::forward<F>(f)));
+            using result_type=typename lift_type<F_result_type>::type;
+
+            if (!set) return result_type();
+            else return bind_impl<result_type,std::is_same<F_result_type,void>::value>::bind(data,std::forward<F>(f));
+        }
+
+        template <typename F>
+        auto bind(F &&f) const -> typename lift_type<decltype(data.apply(std::forward<F>(f)))>::type {
+            using F_result_type=decltype(data.apply(std::forward<F>(f)));
+            using result_type=typename lift_type<F_result_type>::type;
+
+            if (!set) return result_type();
+            else return bind_impl<result_type,std::is_same<F_result_type,void>::value>::bind(data,std::forward<F>(f));
+        }
+
+        template <typename F>
+        auto operator>>(F &&f) -> decltype(this->bind(std::forward<F>(f))) { return bind(std::forward<F>(f)); }
+
+        template <typename F>
+        auto operator>>(F &&f) const -> decltype(this->bind(std::forward<F>(f))) { return bind(std::forward<F>(f)); }
+
+    private:
+        template <typename R,bool F_void_return>
+        struct bind_impl {
+            template <typename DT,typename F>
+            static R bind(DT &d,F &&f) { return R(d.apply(std::forward<F>(f))); }
+        };
+
+        template <typename R>
+        struct bind_impl<R,true> {
+            template <typename DT,typename F>
+            static R bind(DT &d,F &&f) { d.apply(std::forward<F>(f)); return R(true); }
+        };
+        
+    };
+}
+
+template <typename X>
+struct optional: detail::optional_base<X> {
+    using base=detail::optional_base<X>;
+    using base::set;
+    using base::ref;
+    using base::reset;
+    using base::data;
+
+    optional(): base() {}
+    optional(nothing_t): base() {}
+
+    template <typename Y=X,typename = typename std::enable_if<std::is_copy_constructible<Y>::value>::type>
+    optional(const X &x): base(true,x) {}
+
+    template <typename Y=X,typename = typename std::enable_if<std::is_move_constructible<Y>::value>::type>
+    optional(X &&x): base(true,std::move(x)) {}
+
+    optional(const optional &ot): base(ot.set,ot.ref()) {}
+
+    template <typename T>
+    optional(const optional<T> &ot): base(ot.set,ot.ref()) {}
+
+    optional(optional &&ot): base(ot.set,std::move(ot.ref())) {}
+
+    template <typename T>
+    optional(optional<T> &&ot): base(ot.set,std::move(ot.ref())) {}
+
+    template <typename Y,typename = typename std::enable_if<!detail::is_optional<Y>::value>::type>
+    optional &operator=(Y &&y) {
+        if (set) ref()=std::forward<Y>(y);
+        else {
+            set=true;
+            data.construct(std::forward<Y>(y));
+        }
+        return *this;
+    }
+
+    optional &operator=(const optional &o) {
+        if (set) {
+            if (o.set) ref()=o.ref();
+            else reset();
+        }
+        else {
+            set=o.set;
+            if (set) data.construct(o.ref());
+        }
+        return *this;
+    }
+
+    template <typename Y=X, typename =typename std::enable_if<
+        std::is_move_assignable<Y>::value &&
+        std::is_move_constructible<Y>::value
+    >::type>
+    optional &operator=(optional &&o) {
+        if (set) {
+            if (o.set) ref()=std::move(o.ref());
+            else reset();
+        }
+        else {
+            set=o.set;
+            if (set) data.construct(std::move(o.ref()));
+        }
+        return *this;
+    }
+};
+
+template <typename X>
+struct optional<X &>: detail::optional_base<X &> {
+    using base=detail::optional_base<X &>;
+    using base::set;
+    using base::ref;
+    using base::data;
+
+    optional(): base() {}
+    optional(nothing_t): base() {}
+    optional(X &x): base(true,x) {}
+
+    template <typename T>
+    optional(optional<T &> &ot): base(ot.set,ot.ref()) {}
+
+    template <typename Y,typename = typename std::enable_if<!detail::is_optional<Y>::value>::type>
+    optional &operator=(Y &y) {
+        set=true;
+        ref()=y;
+        return *this;
+    }
+
+    template <typename Y>
+    optional &operator=(optional<Y &> &o) {
+        set=o.set;
+        data.construct(o);
+        return *this;
+    }
+};
+
+
+/* special case for optional<void>, used as e.g. the result of
+ * binding to a void function */
+
+template <>
+struct optional<void>: detail::optional_base<void> {
+    using base=detail::optional_base<void>;
+    using base::set;
+
+    optional(): base() {}
+
+    template <typename T>
+    optional(T): base(true,true) {}
+
+    template <typename T>
+    optional(const optional<T> &o): base(o.set,true) {}
+    
+    template <typename T>
+    optional &operator=(T) { set=true; return *this; }
+
+    template <typename T>
+    optional &operator=(const optional<T> &o) { set=o.set; return *this; }
+
+    // override equality operators
+    template <typename Y>
+    bool operator==(const Y &y) const { return false; }
+
+    bool operator==(const optional<void> &o) const {
+        return (set && o.set) || (!set && !o.set);
+    }
+};
+
+
+template <typename A,typename B>
+typename std::enable_if<
+    detail::is_optional<A>::value || detail::is_optional<B>::value,
+    optional<typename std::common_type<typename detail::wrapped_type<A>::type,typename detail::wrapped_type<B>::type>::type>
+>::type
+operator|(A &&a,B &&b) {
+    return a?a:b;
+}
+
+template <typename A,typename B>
+typename std::enable_if<
+    detail::is_optional<A>::value || detail::is_optional<B>::value,
+    optional<typename detail::wrapped_type<B>::type>
+>::type
+operator&(A &&a,B &&b) {
+    using result_type=optional<typename detail::wrapped_type<B>::type>;
+    return a?b:result_type();
+}
+
+inline optional<void> provided(bool condition) { return condition?optional<void>(true):optional<void>(); }
+
+template <typename X>
+optional<X> just(X &&x) { return optional<X>(std::forward<X>(x)); }
+
+}}} // namespace nest::mc::util
diff --git a/src/util/uninitialized.hpp b/src/util/uninitialized.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..26ebcf90ef2c94ea0645ccb7d3a94f6a01fb2510
--- /dev/null
+++ b/src/util/uninitialized.hpp
@@ -0,0 +1,128 @@
+#pragma once
+
+/* Represent a possibly-uninitialized value, reference or void.
+ *
+ * The uninitialized<X> structure holds space for an item of
+ * type X, leaving its construction or destruction to the user.
+ * 
+ * Specialisations for reference types X & and for the void type
+ * allow for the handling of non-value types in a uniform manner.
+ */
+
+namespace nest {
+namespace mc {
+namespace util {
+
+/* Maintains storage for a value of type X, with explicit
+ * construction and destruction.
+ */
+template <typename X>
+struct uninitialized {
+private:
+    typename std::aligned_storage<sizeof(X),alignof(X)>::type data;
+
+public:
+    using pointer_type=X *;
+    using const_pointer_type=const X *;
+    using reference_type=X &;
+    using const_reference_type=const X &;
+
+    pointer_type ptr() { return reinterpret_cast<X *>(&data); }
+    const_pointer_type cptr() const { return reinterpret_cast<const X *>(&data); }
+
+    reference_type ref() { return *reinterpret_cast<X *>(&data); }
+    const_reference_type cref() const { return *reinterpret_cast<const X *>(&data); }
+
+    // Copy construct the value.
+    template <typename Y=X,
+              typename =typename std::enable_if<std::is_copy_constructible<Y>::value>::type>
+    void construct(const X &x) { new(&data) X(x); }
+
+    // General constructor for X, forwarding arguments.
+    template <typename... Y,
+              typename =typename std::enable_if<std::is_constructible<X,Y...>::value>::type>
+    void construct(Y&& ...args) { new(&data) X(std::forward<Y>(args)...); }
+
+    void destruct() { ptr()->~X(); }
+
+    // Apply the one-parameter functor F to the value by reference.
+    template <typename F>
+    typename std::result_of<F(reference_type)>::type apply(F &&f) { return f(ref()); }
+
+    // Apply the one-parameter functor F to the value by const reference.
+    template <typename F>
+    typename std::result_of<F(const_reference_type)>::type apply(F &&f) const { return f(cref()); }
+};
+
+/* Maintains storage for a pointer of type X, representing
+ * a possibly uninitialized reference.
+ */
+template <typename X>
+struct uninitialized<X&> {
+private:
+    X *data;
+
+public:
+    using pointer_type=X *;
+    using const_pointer_type=const X *;
+    using reference_type=X &;
+    using const_reference_type=const X &;
+
+    pointer_type ptr() { return data; }
+    const_pointer_type cptr() const { return data; }
+
+    reference_type ref() { return *data; }
+    const_reference_type cref() const { return *data; }
+
+    void construct(X &x) { data=&x; }
+    void destruct() {}
+
+    // Apply the one-parameter functor F to the value by reference.
+    template <typename F>
+    typename std::result_of<F(reference_type)>::type apply(F &&f) { return f(ref()); }
+    // Apply the one-parameter functor F to the value by const reference.
+    template <typename F>
+    typename std::result_of<F(const_reference_type)>::type apply(F &&f) const { return f(cref()); }
+};
+
+/* Wrap a void type in an uninitialized template.
+ * 
+ * Allows the use of uninitialized<X> for void X, for generic applications.
+ */
+template <>
+struct uninitialized<void> {
+    using pointer_type=void *;
+    using const_pointer_type=const void *;
+    using reference_type=void;
+    using const_reference_type=void;
+
+    pointer_type ptr() { return nullptr; }
+    const_pointer_type cptr() const { return nullptr; }
+
+    reference_type ref() {}
+    const_reference_type cref() const {}
+
+    // No operation.
+    void construct(...) {}
+    // No operation.
+    void destruct() {}
+
+    // Equivalent to f()
+    template <typename F>
+    typename std::result_of<F()>::type apply(F &&f) const { return f(); }
+};
+
+template <typename...>
+struct uninitialized_can_construct: std::false_type {};
+
+template <typename X,typename... Y>
+struct uninitialized_can_construct<X,Y...>: std::integral_constant<bool,std::is_constructible<X,Y...>::value> {};
+
+template <typename X,typename Y>
+struct uninitialized_can_construct<X &,Y>: std::integral_constant<bool,std::is_convertible<X &,Y>::value> {};
+
+template <typename... Y>
+struct uninitialized_can_construct<void,Y...>: std::true_type {};
+
+}}} // namespace nest::mc::util
+
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 9b61220058ee4f83d4102c68102dd85c7bfa9ece..33e4439834b902c2ad775dc157e03fb452c8f8ac 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -20,6 +20,7 @@ set(TEST_SOURCES
     test_fvm.cpp
     test_matrix.cpp
     test_mechanisms.cpp
+    test_optional.cpp
     test_parameters.cpp
     test_point.cpp
     test_segment.cpp
@@ -27,6 +28,7 @@ set(TEST_SOURCES
     test_swcio.cpp
     test_synapses.cpp
     test_tree.cpp
+    test_uninitialized.cpp
 
     # unit test driver
     test.cpp
diff --git a/tests/test_event_queue.cpp b/tests/test_event_queue.cpp
index 3297784d506f6eff10078e249388942b85d2240f..e67ade0bea22a417ca417a78c550b67bb0c3e45b 100644
--- a/tests/test_event_queue.cpp
+++ b/tests/test_event_queue.cpp
@@ -18,7 +18,7 @@ TEST(event_queue, push)
     std::vector<float> times;
     while(q.size()) {
         times.push_back(
-            q.pop_if_before(std::numeric_limits<float>::max()).second.time
+            q.pop_if_before(std::numeric_limits<float>::max())->time
         );
     }
 
@@ -44,7 +44,7 @@ TEST(event_queue, push_range)
     std::vector<float> times;
     while(q.size()) {
         times.push_back(
-            q.pop_if_before(std::numeric_limits<float>::max()).second.time
+            q.pop_if_before(std::numeric_limits<float>::max())->time
         );
     }
 
@@ -68,26 +68,26 @@ TEST(event_queue, pop_if_before)
     EXPECT_EQ(q.size(), 4u);
 
     auto e1 = q.pop_if_before(0.);
-    EXPECT_FALSE(e1.first);
+    EXPECT_FALSE(e1);
     EXPECT_EQ(q.size(), 4u);
 
     auto e2 = q.pop_if_before(5.);
-    EXPECT_TRUE(e2.first);
-    EXPECT_EQ(e2.second.target, 1u);
+    EXPECT_TRUE(e2);
+    EXPECT_EQ(e2->target, 1u);
     EXPECT_EQ(q.size(), 3u);
 
     auto e3 = q.pop_if_before(5.);
-    EXPECT_TRUE(e3.first);
-    EXPECT_EQ(e3.second.target, 2u);
+    EXPECT_TRUE(e3);
+    EXPECT_EQ(e3->target, 2u);
     EXPECT_EQ(q.size(), 2u);
 
     auto e4 = q.pop_if_before(2.5);
-    EXPECT_FALSE(e4.first);
+    EXPECT_FALSE(e4);
     EXPECT_EQ(q.size(), 2u);
 
     auto e5 = q.pop_if_before(5.);
-    EXPECT_TRUE(e5.first);
-    EXPECT_EQ(e5.second.target, 3u);
+    EXPECT_TRUE(e5);
+    EXPECT_EQ(e5->target, 3u);
     EXPECT_EQ(q.size(), 1u);
 
     q.pop_if_before(5.);
@@ -95,5 +95,5 @@ TEST(event_queue, pop_if_before)
 
     // empty queue should always return "false"
     auto e6 = q.pop_if_before(100.);
-    EXPECT_FALSE(e6.first);
+    EXPECT_FALSE(e6);
 }
diff --git a/tests/test_optional.cpp b/tests/test_optional.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e3ca5ecbbd7466c7f5e7b6a9c2f7bcbef43dd5b1
--- /dev/null
+++ b/tests/test_optional.cpp
@@ -0,0 +1,335 @@
+#include <typeinfo>
+#include <array>
+#include <algorithm>
+
+#include "gtest.h"
+#include "util/optional.hpp"
+
+using namespace nest::mc::util;
+
+TEST(optionalm,ctors) {
+    optional<int> a,b(3),c=b,d=4;
+
+    ASSERT_FALSE((bool)a);
+    ASSERT_TRUE((bool)b);
+    ASSERT_TRUE((bool)c);
+    ASSERT_TRUE((bool)d);
+
+    EXPECT_EQ(3,b.get());
+    EXPECT_EQ(3,c.get());
+    EXPECT_EQ(4,d.get());
+}
+
+TEST(optionalm,unset_throw) {
+    optional<int> a;
+    int check=10;
+
+    try { a.get(); }
+    catch(optional_unset_error &e) {
+        ++check;
+    }
+    EXPECT_EQ(11,check);
+
+    check=20;
+    a=2;
+    try { a.get(); }
+    catch(optional_unset_error &e) {
+        ++check;
+    }
+    EXPECT_EQ(20,check);
+
+    check=30;
+    a.reset();
+    try { a.get(); }
+    catch(optional_unset_error &e) {
+        ++check;
+    }
+    EXPECT_EQ(31,check);
+}
+
+TEST(optionalm,deref) {
+    struct foo {
+        int a;
+        explicit foo(int a_): a(a_) {}
+        double value() { return 3.0*a; }
+    };
+    
+    optional<foo> f=foo(2);
+    EXPECT_EQ(6.0,f->value());
+    EXPECT_EQ(2,(*f).a);
+}
+
+TEST(optionalm,ctor_conv) {
+    optional<std::array<int,3>> x{{1,2,3}};
+    EXPECT_EQ(3,x->size());
+}
+
+TEST(optionalm,ctor_ref) {
+    int v=10;
+    optional<int &> a(v);
+
+    EXPECT_EQ(10,a.get());
+    v=20;
+    EXPECT_EQ(20,a.get());
+
+    optional<int &> b(a),c=b,d=v;
+    EXPECT_EQ(&(a.get()),&(b.get()));
+    EXPECT_EQ(&(a.get()),&(c.get()));
+    EXPECT_EQ(&(a.get()),&(d.get()));
+}
+
+TEST(optionalm,assign_returns) {
+    optional<int> a=3;
+
+    auto b=(a=4);
+    EXPECT_EQ(typeid(optional<int>),typeid(b));
+
+    auto bp=&(a=4);
+    EXPECT_EQ(&a,bp);
+}
+
+namespace {
+    struct nomove {
+        int value;
+
+        nomove(): value(0) {}
+        nomove(int i): value(i) {}
+        nomove(const nomove &n): value(n.value) {}
+        nomove(nomove &&n) = delete;
+
+        nomove &operator=(const nomove &n) { value=n.value; return *this; }
+
+        bool operator==(const nomove &them) const { return them.value==value; }
+        bool operator!=(const nomove &them) const { return !(*this==them); }
+    };
+}
+ 
+TEST(optionalm,ctor_nomove) {
+    optional<nomove> a(nomove(3));
+    EXPECT_EQ(nomove(3),a.get());
+
+    optional<nomove> b;
+    b=a;
+    EXPECT_EQ(nomove(3),b.get());
+
+    b=optional<nomove>(nomove(4));
+    EXPECT_EQ(nomove(4),b.get());
+}
+
+namespace {
+    struct nocopy {
+        int value;
+
+        nocopy(): value(0) {}
+        nocopy(int i): value(i) {}
+        nocopy(const nocopy &n) = delete;
+        nocopy(nocopy &&n) {
+            value=n.value;
+            n.value=0;
+        }
+
+        nocopy &operator=(const nocopy &n) = delete;
+        nocopy &operator=(nocopy &&n) {
+            value=n.value;
+            n.value=-1;
+            return *this;
+        }
+
+        bool operator==(const nocopy &them) const { return them.value==value; }
+        bool operator!=(const nocopy &them) const { return !(*this==them); }
+    };
+}
+    
+TEST(optionalm,ctor_nocopy) {
+    optional<nocopy> a(nocopy(5));
+    EXPECT_EQ(nocopy(5),a.get());
+
+    optional<nocopy> b(std::move(a));
+    EXPECT_EQ(nocopy(5),b.get());
+    EXPECT_EQ(0,a.get().value);
+
+    b=optional<nocopy>(nocopy(6));
+    EXPECT_EQ(nocopy(6),b.get());
+}
+
+namespace {
+    optional<double> odd_half(int n) {
+        optional<double> h;
+        if (n%2==1) h=n/2.0;
+        return h;
+    }
+}
+
+TEST(optionalm,bind) {
+    optional<int> a;
+    auto b=a.bind(odd_half);
+
+    EXPECT_EQ(typeid(optional<double>),typeid(b));
+
+    a=10;
+    b=a.bind(odd_half);
+    EXPECT_FALSE((bool)b);
+
+    a=11;
+    b=a.bind(odd_half);
+    EXPECT_TRUE((bool)b);
+    EXPECT_EQ(5.5,b.get());
+
+    b=a >> odd_half >> [](double x) { return (int)x; } >> odd_half;
+    EXPECT_TRUE((bool)b);
+    EXPECT_EQ(2.5,b.get());
+}
+
+TEST(optionalm,void) {
+    optional<void> a,b(true),c(a),d=b,e(false);
+
+    EXPECT_FALSE((bool)a);
+    EXPECT_TRUE((bool)b);
+    EXPECT_FALSE((bool)c);
+    EXPECT_TRUE((bool)d);
+    EXPECT_TRUE((bool)e);
+
+    auto x=a >> []() { return 1; };
+    EXPECT_FALSE((bool)x);
+
+    x=b >> []() { return 1; };
+    EXPECT_TRUE((bool)x);
+    EXPECT_EQ(1,x.get());
+}
+
+TEST(optionalm,bind_to_void) {
+    optional<int> a,b(3);
+    
+    int call_count=0;
+    auto vf=[&call_count](int i) -> void { ++call_count; };
+
+    auto x=a >> vf;
+    EXPECT_EQ(typeid(optional<void>),typeid(x));
+    EXPECT_FALSE((bool)x);
+    EXPECT_EQ(0,call_count);
+
+    call_count=0;
+    x=b >> vf;
+    EXPECT_TRUE((bool)x);
+    EXPECT_EQ(1,call_count);
+}
+    
+TEST(optionalm,bind_to_optional_void) {
+    optional<int> a,b(3),c(4);
+    
+    int count=0;
+    auto count_if_odd=[&count](int i) { return i%2?(++count,optional<void>(true)):optional<void>(); };
+
+    auto x=a >> count_if_odd;
+    EXPECT_EQ(typeid(optional<void>),typeid(x));
+    EXPECT_FALSE((bool)x);
+    EXPECT_EQ(0,count);
+
+    count=0;
+    x=b >> count_if_odd;
+    EXPECT_TRUE((bool)x);
+    EXPECT_EQ(1,count);
+
+    count=0;
+    x=c >> count_if_odd;
+    EXPECT_FALSE((bool)x);
+    EXPECT_EQ(0,count);
+}
+
+TEST(optionalm,bind_with_ref) {
+    optional<int> a=10;
+    a >> [](int &v) {++v; };
+    EXPECT_EQ(11,*a);
+}
+
+namespace {
+    struct check_cref {
+        int operator()(const int &) { return 10; }
+        int operator()(int &) { return 11; }
+    };
+}
+
+TEST(optionalm,bind_constness) {
+    check_cref checker;
+    optional<int> a=1;
+    int v=*(a >> checker);
+    EXPECT_EQ(11,v);
+
+    const optional<int> b=1;
+    v=*(b >> checker);
+    EXPECT_EQ(10,v);
+}
+
+
+TEST(optionalm,conversion) {
+    optional<double> a(3),b=5;
+    EXPECT_TRUE((bool)a);
+    EXPECT_TRUE((bool)b);
+    EXPECT_EQ(3.0,a.get());
+    EXPECT_EQ(5.0,b.get());
+
+    optional<int> x;
+    optional<double> c(x);
+    optional<double> d=optional<int>();
+    EXPECT_FALSE((bool)c);
+    EXPECT_FALSE((bool)d);
+
+    auto doubler=[](double x) { return x*2; };
+    auto y=optional<int>(3) >> doubler;
+    EXPECT_TRUE((bool)y);
+    EXPECT_EQ(6.0,y.get());
+}
+
+TEST(optionalm,or_operator) {
+    optional<const char *> default_msg="default";
+    auto x=nullptr | default_msg;
+    EXPECT_TRUE((bool)x);
+    EXPECT_STREQ("default",x.get());
+
+    auto y="something" | default_msg;
+    EXPECT_TRUE((bool)y);
+    EXPECT_STREQ("something",y.get());
+
+    optional<int> a(1),b,c(3);
+    EXPECT_EQ(1,*(a|b|c));
+    EXPECT_EQ(1,*(a|c|b));
+    EXPECT_EQ(1,*(b|a|c));
+    EXPECT_EQ(3,*(b|c|a));
+    EXPECT_EQ(3,*(c|a|b));
+    EXPECT_EQ(3,*(c|b|a));
+}
+
+TEST(optionalm,and_operator) {
+    optional<int> a(1);
+    optional<double> b(2.0);
+
+    auto ab=a&b;
+    auto ba=b&a;
+
+    EXPECT_EQ(typeid(ab),typeid(b));
+    EXPECT_EQ(typeid(ba),typeid(a));
+    EXPECT_EQ(2.0,*ab);
+    EXPECT_EQ(1,*ba);
+
+    auto zb=false & b;
+    EXPECT_EQ(typeid(zb),typeid(b));
+    EXPECT_FALSE((bool)zb);
+    
+    auto b3=b & 3;
+    EXPECT_EQ(typeid(b3),typeid(optional<int>));
+    EXPECT_TRUE((bool)b3);
+    EXPECT_EQ(3,*b3);
+}
+
+TEST(optionalm,provided) {
+    std::array<int,3> qs={1,0,3};
+    std::array<int,3> ps={14,14,14};
+    std::array<int,3> rs;
+
+    std::transform(ps.begin(),ps.end(),qs.begin(),rs.begin(),
+        [](int p,int q) { return *( provided(q!=0) >> [=]() { return p/q; } | -1 ); });
+
+    EXPECT_EQ(14,rs[0]);
+    EXPECT_EQ(-1,rs[1]);
+    EXPECT_EQ(4,rs[2]);
+}
diff --git a/tests/test_uninitialized.cpp b/tests/test_uninitialized.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dcc9e47ce6a7fa266376e054e8f84a04d8ec818d
--- /dev/null
+++ b/tests/test_uninitialized.cpp
@@ -0,0 +1,200 @@
+#include "gtest.h"
+
+#include "util/uninitialized.hpp"
+
+using namespace nest::mc::util;
+
+namespace {
+    struct count_ops {
+        count_ops() {}
+        count_ops(const count_ops &n) { ++copy_ctor_count; }
+        count_ops(count_ops &&n) { ++move_ctor_count; }
+
+        count_ops &operator=(const count_ops &n) { ++copy_assign_count; return *this; }
+        count_ops &operator=(count_ops &&n) { ++move_assign_count; return *this; }
+
+        static int copy_ctor_count,copy_assign_count;
+        static int move_ctor_count,move_assign_count;
+        static void reset_counts() {
+            copy_ctor_count=copy_assign_count=0; 
+            move_ctor_count=move_assign_count=0;
+        }
+    };
+
+    int count_ops::copy_ctor_count=0;
+    int count_ops::copy_assign_count=0;
+    int count_ops::move_ctor_count=0;
+    int count_ops::move_assign_count=0;
+}
+
+TEST(uninitialized,ctor) {
+    count_ops::reset_counts();
+
+    uninitialized<count_ops> ua;
+    ua.construct(count_ops{});
+
+    count_ops b;
+    ua.construct(b);
+
+    EXPECT_EQ(1,count_ops::copy_ctor_count);
+    EXPECT_EQ(0,count_ops::copy_assign_count);
+    EXPECT_EQ(1,count_ops::move_ctor_count);
+    EXPECT_EQ(0,count_ops::move_assign_count);
+
+    ua.ref()=count_ops{};
+    ua.ref()=b;
+
+    EXPECT_EQ(1,count_ops::copy_ctor_count);
+    EXPECT_EQ(1,count_ops::copy_assign_count);
+    EXPECT_EQ(1,count_ops::move_ctor_count);
+    EXPECT_EQ(1,count_ops::move_assign_count);
+}
+
+namespace {
+    struct nocopy {
+        nocopy() {}
+        nocopy(const nocopy &n) = delete;
+        nocopy(nocopy &&n) { ++move_ctor_count; }
+
+        nocopy &operator=(const nocopy &n) = delete;
+        nocopy &operator=(nocopy &&n) { ++move_assign_count; return *this; }
+
+        static int move_ctor_count,move_assign_count;
+        static void reset_counts() { move_ctor_count=move_assign_count=0; }
+    };
+
+    int nocopy::move_ctor_count=0;
+    int nocopy::move_assign_count=0;
+}
+
+TEST(uninitialized,ctor_nocopy) {
+    nocopy::reset_counts();
+
+    uninitialized<nocopy> ua;
+    ua.construct(nocopy{});
+
+    EXPECT_EQ(1,nocopy::move_ctor_count);
+    EXPECT_EQ(0,nocopy::move_assign_count);
+
+    ua.ref()=nocopy{};
+
+    EXPECT_EQ(1,nocopy::move_ctor_count);
+    EXPECT_EQ(1,nocopy::move_assign_count);
+}
+
+namespace {
+    struct nomove {
+        nomove() {}
+        nomove(const nomove &n) { ++copy_ctor_count; }
+        nomove(nomove &&n) = delete;
+
+        nomove &operator=(const nomove &n) { ++copy_assign_count; return *this; }
+        nomove &operator=(nomove &&n) = delete;
+
+        static int copy_ctor_count,copy_assign_count;
+        static void reset_counts() { copy_ctor_count=copy_assign_count=0; }
+    };
+
+    int nomove::copy_ctor_count=0;
+    int nomove::copy_assign_count=0;
+}
+
+TEST(uninitialized,ctor_nomove) {
+    nomove::reset_counts();
+
+    uninitialized<nomove> ua;
+    ua.construct(nomove{}); // check against rvalue
+
+    nomove b;
+    ua.construct(b); // check against non-const lvalue
+
+    const nomove c;
+    ua.construct(c); // check against const lvalue
+
+    EXPECT_EQ(3,nomove::copy_ctor_count);
+    EXPECT_EQ(0,nomove::copy_assign_count);
+
+    nomove a;
+    ua.ref()=a;
+
+    EXPECT_EQ(3,nomove::copy_ctor_count);
+    EXPECT_EQ(1,nomove::copy_assign_count);
+}
+
+TEST(uninitialized,void) {
+    uninitialized<void> a,b;
+    a=b;
+
+    EXPECT_EQ(typeid(a.ref()),typeid(void));
+}
+
+TEST(uninitialized,ref) {
+    uninitialized<int &> x,y;
+    int a;
+
+    x.construct(a);
+    y=x;
+
+    x.ref()=2;
+    EXPECT_EQ(2,a);
+
+    y.ref()=3;
+    EXPECT_EQ(3,a);
+    EXPECT_EQ(3,x.cref());
+
+    EXPECT_EQ(&a,x.ptr());
+    EXPECT_EQ((const int *)&a,x.cptr());
+}
+
+namespace {
+    struct apply_tester {
+        mutable int op_count=0;
+        mutable int const_op_count=0;
+
+        int operator()(const int &a) const { ++const_op_count; return a+1; }
+        int operator()(int &a) const { ++op_count; return ++a; }
+    };
+}
+
+TEST(uninitialized,apply) {
+    uninitialized<int> ua;
+    ua.construct(10);
+
+    apply_tester A;
+    int r=ua.apply(A);
+    EXPECT_EQ(11,ua.cref());
+    EXPECT_EQ(11,r);
+
+    uninitialized<int &> ub;
+    ub.construct(ua.ref());
+
+    r=ub.apply(A);
+    EXPECT_EQ(12,ua.cref());
+    EXPECT_EQ(12,r);
+
+    uninitialized<const int &> uc;
+    uc.construct(ua.ref());
+
+    r=uc.apply(A);
+    EXPECT_EQ(12,ua.cref());
+    EXPECT_EQ(13,r);
+
+    const uninitialized<int> ud(ua);
+
+    r=ud.apply(A);
+    EXPECT_EQ(12,ua.cref());
+    EXPECT_EQ(12,ud.cref());
+    EXPECT_EQ(13,r);
+
+    EXPECT_EQ(2,A.op_count);
+    EXPECT_EQ(2,A.const_op_count);
+}
+
+TEST(uninitialized,void_apply) {
+    uninitialized<void> uv;
+
+    auto f=[]() { return 11; };
+    EXPECT_EQ(11,uv.apply(f));
+
+    EXPECT_EQ(12.5,uv.apply([]() { return 12.5; }));
+}