diff --git a/lmorpho/lmorpho.cpp b/lmorpho/lmorpho.cpp
index 418b67ad5066a57d6e47011d64fdf43f603c7439..efe7615175257ac68fdb509fdf63b5002a43ac8e 100644
--- a/lmorpho/lmorpho.cpp
+++ b/lmorpho/lmorpho.cpp
@@ -15,7 +15,7 @@
 
 namespace to = arb::to;
 using arb::util::optional;
-using arb::util::nothing;
+using arb::util::nullopt;
 using arb::util::just;
 
 const char* usage_str =
@@ -31,7 +31,7 @@ const char* usage_str =
 "\n"
 "Generate artificial neuron morphologies based on L-system descriptions.\n"
 "\n"
-"If a FILE arrgument contains a '%', then one file will be written for\n"
+"If a FILE argument contains a '%', then one file will be written for\n"
 "each generated morphology, with the '%' replaced by the index of the\n"
 "morphology, starting from zero. Output for each morphology will otherwise\n"
 "be concatenated: SWC files will be headed by a comment line with the\n"
@@ -89,10 +89,10 @@ int main(int argc, char** argv) {
         }
 
         std::minstd_rand g;
-        if (rng_seed) g.seed(rng_seed.get());
+        if (rng_seed) g.seed(rng_seed.value());
 
-        auto emit_swc = swc_file? just(swc_emitter(*swc_file, n_morph)): nothing;
-        auto emit_pvec = pvector_file? just(pvector_emitter(*pvector_file, n_morph)): nothing;
+        auto emit_swc = swc_file? just(swc_emitter(*swc_file, n_morph)): nullopt;
+        auto emit_pvec = pvector_file? just(pvector_emitter(*pvector_file, n_morph)): nullopt;
 
         for (int i=0; i<n_morph; ++i) {
             auto morph = generate_morphology(P, g);
diff --git a/miniapps/miniapp/io.cpp b/miniapps/miniapp/io.cpp
index b90d56bbae72f7722adff758e777e857744dc251..052bc5a52fb8c33b45bdb44f241f53af9e040472 100644
--- a/miniapps/miniapp/io.cpp
+++ b/miniapps/miniapp/io.cpp
@@ -105,7 +105,7 @@ static void update_option(util::optional<T>& opt, const nlohmann::json& j, const
     if (j.count(key)) {
         auto value = j[key];
         if (value.is_null()) {
-            opt = util::nothing;
+            opt = util::nullopt;
         }
         else {
             opt = value.get<T>();
@@ -330,14 +330,14 @@ cl_options read_options(int argc, char** argv, bool allow_write) {
                 fopts["probe_soma_only"] = options.probe_soma_only;
                 fopts["trace_prefix"] = options.trace_prefix;
                 if (options.trace_max_gid) {
-                    fopts["trace_max_gid"] = options.trace_max_gid.get();
+                    fopts["trace_max_gid"] = options.trace_max_gid.value();
                 }
                 else {
                     fopts["trace_max_gid"] = nullptr;
                 }
                 fopts["trace_format"] = options.trace_format;
                 if (options.morphologies) {
-                    fopts["morphologies"] = options.morphologies.get();
+                    fopts["morphologies"] = options.morphologies.value();
                 }
                 else {
                     fopts["morphologies"] = nullptr;
diff --git a/miniapps/miniapp/miniapp.cpp b/miniapps/miniapp/miniapp.cpp
index b79b456b879ccf6680e63ba8bfa7bcfbbf571b20..cc34177b8eae7e55e92156a6beb7887bdceef893 100644
--- a/miniapps/miniapp/miniapp.cpp
+++ b/miniapps/miniapp/miniapp.cpp
@@ -208,7 +208,7 @@ std::unique_ptr<recipe> make_recipe(const io::cl_options& options, const probe_d
     if (options.morphologies) {
         std::cout << "loading morphologies...\n";
         p.morphologies.clear();
-        load_swc_morphology_glob(p.morphologies, options.morphologies.get());
+        load_swc_morphology_glob(p.morphologies, options.morphologies.value());
         std::cout << "loading morphologies: " << p.morphologies.size() << " loaded.\n";
     }
     p.morphology_round_robin = options.morph_rr;
diff --git a/miniapps/miniapp/miniapp_recipes.cpp b/miniapps/miniapp/miniapp_recipes.cpp
index 9ab4cf3ae0a46f36fb769a34f3de7bdf0ae7fec1..017236d425a05820d39a64debfbb9cb146a46c06 100644
--- a/miniapps/miniapp/miniapp_recipes.cpp
+++ b/miniapps/miniapp/miniapp_recipes.cpp
@@ -91,7 +91,7 @@ public:
         // or a spikes from file.
         if (i == ncell_) {
             if (param_.input_spike_path) {
-                auto spike_times = io::get_parsed_spike_times_from_path(param_.input_spike_path.get());
+                auto spike_times = io::get_parsed_spike_times_from_path(param_.input_spike_path.value());
                 return util::unique_any(dss_cell_description(spike_times));
             }
 
diff --git a/modcc/cprinter.cpp b/modcc/cprinter.cpp
index 02a29a9b3c57f9db579cd3e91f973fc4ac02e974..bb855053f791acbe171c6423ebb1917fe12433e7 100644
--- a/modcc/cprinter.cpp
+++ b/modcc/cprinter.cpp
@@ -367,7 +367,7 @@ std::string CPrinter::emit_source() {
     text_.add_line("};");
     text_.add_line();
     text_.add_line("auto* info = util::table_lookup(field_tbl, id);");
-    text_.add_line("return info? util::just(*info): util::nothing;");
+    text_.add_line("return info? util::just(*info): util::nullopt;");
     text_.decrease_indentation();
     text_.add_line("}");
     text_.add_line();
diff --git a/src/event_binner.cpp b/src/event_binner.cpp
index 0b380bd4fd5263eb8a9ddf9c3c6aac0affc027ab..1b5d8c0a1f3bb2828f874cec768c3ababbb4a9ac 100644
--- a/src/event_binner.cpp
+++ b/src/event_binner.cpp
@@ -12,7 +12,7 @@
 namespace arb {
 
 void event_binner::reset() {
-    last_event_time_ = util::nothing;
+    last_event_time_ = util::nullopt;
 }
 
 time_type event_binner::bin(time_type t, time_type t_min) {
diff --git a/src/event_queue.hpp b/src/event_queue.hpp
index 5675e1d1719e7e8f60bcea21e2bbbb4ea3607362..0d3fe5535954e7275408bb4b10c2c0bc91f867a2 100644
--- a/src/event_queue.hpp
+++ b/src/event_queue.hpp
@@ -73,12 +73,12 @@ public :
     // Return time t of head of queue if `t_until` > `t`.
     util::optional<event_time_type> time_if_before(const event_time_type& t_until) {
         if (queue_.empty()) {
-            return util::nothing;
+            return util::nullopt;
         }
 
         using ::arb::event_time;
         auto t = event_time(queue_.top());
-        return t_until > t? util::just(t): util::nothing;
+        return t_until > t? util::just(t): util::nullopt;
     }
 
     // Generic conditional pop: pop and return head of queue if
@@ -92,7 +92,7 @@ public :
             return ev;
         }
         else {
-            return util::nothing;
+            return util::nullopt;
         }
     }
 
diff --git a/src/fvm_multicell.hpp b/src/fvm_multicell.hpp
index 4a4dd52a03a5db1f501e4c094a018aebac73ef3e..3eb1fb1a73ebec1b077c5c6d5c484476d0b5e232 100644
--- a/src/fvm_multicell.hpp
+++ b/src/fvm_multicell.hpp
@@ -257,7 +257,7 @@ public:
         auto it = std::find_if(
             std::begin(mechanisms_), std::end(mechanisms_),
             [&name](const mechanism_ptr& m) {return m->name()==name;});
-        return it==mechanisms_.end() ? util::nothing: util::just(*it);
+        return it==mechanisms_.end() ? util::nullopt: util::just(*it);
     }
 
     //
diff --git a/src/hardware/affinity.cpp b/src/hardware/affinity.cpp
index b36f34e8c05e6f3735d110b5fae64b87cbb42999..54b5f47b7e68ed1f7d455597b7ae521f6b97cf39 100644
--- a/src/hardware/affinity.cpp
+++ b/src/hardware/affinity.cpp
@@ -54,7 +54,7 @@ std::vector<int> get_affinity() {
 util::optional<std::size_t> num_cores() {
     auto cores = get_affinity();
     if (cores.size()==0u) {
-        return util::nothing;
+        return util::nullopt;
     }
     return cores.size();
 }
diff --git a/src/model.cpp b/src/model.cpp
index 87144a3461e63f9076ed4ec7aa1ef4c0326fa555..47519576dcbed4170f0201b9afc8f306bcedf47a 100644
--- a/src/model.cpp
+++ b/src/model.cpp
@@ -243,7 +243,7 @@ void model::set_local_spike_callback(spike_export_function export_callback) {
 util::optional<cell_size_type> model::local_cell_index(cell_gid_type gid) {
     auto it = gid_to_local_.find(gid);
     return it==gid_to_local_.end()?
-        util::nothing:
+        util::nullopt:
         util::optional<cell_size_type>(it->second);
 }
 
diff --git a/src/segment.hpp b/src/segment.hpp
index 66534669b928326ace2263b93acb3c6fcf2f2820..6c2e2e2b107b45b51fe22ab13c2eae026e80ee5f 100644
--- a/src/segment.hpp
+++ b/src/segment.hpp
@@ -83,13 +83,13 @@ public:
     util::optional<mechanism_spec&> mechanism(const std::string& name) {
         auto it = std::find_if(mechanisms_.begin(), mechanisms_.end(),
             [&](mechanism_spec& m) { return m.name()==name; });
-        return it==mechanisms_.end()? util::nothing: util::just(*it);
+        return it==mechanisms_.end()? util::nullopt: util::just(*it);
     }
 
     void add_mechanism(mechanism_spec mech) {
         auto m = mechanism(mech.name());
         if (m) {
-            m.get() = std::move(mech);
+            *m = std::move(mech);
         }
         else {
             mechanisms_.push_back(std::move(mech));
diff --git a/src/threading/threading.cpp b/src/threading/threading.cpp
index 35ebf467872f29ae8b362770a6c7c47179ff37f2..c1cd5f2f98c2ee3a7e564794d7f01462d9a645e0 100644
--- a/src/threading/threading.cpp
+++ b/src/threading/threading.cpp
@@ -40,7 +40,7 @@ util::optional<size_t> get_env_num_threads() {
     // If the selected var is unset set the number of threads to
     // the hint given by the standard library
     if (!str) {
-        return util::nothing;
+        return util::nullopt;
     }
 
     auto nthreads = std::strtoul(str, nullptr, 10);
diff --git a/src/tinyopt.hpp b/src/tinyopt.hpp
index c90ba789bd96079694202bdaf50a0d3abba4419c..ca4366677f1a940075695382735f735f7c05c40f 100644
--- a/src/tinyopt.hpp
+++ b/src/tinyopt.hpp
@@ -42,7 +42,7 @@ struct default_parser {
         V v;
         std::istringstream stream(text);
         stream >> v;
-        return stream? util::just(v): util::nothing;
+        return stream? util::just(v): util::nullopt;
     }
 };
 
@@ -58,7 +58,7 @@ public:
         for (const auto& p: map_) {
             if (text==p.first) return p.second;
         }
-        return util::nothing;
+        return util::nullopt;
     }
 };
 
@@ -72,7 +72,7 @@ util::optional<V> parse_opt(char **& argp, char shortopt, const char* longopt=nu
     const char* arg = argp[0];
 
     if (!arg || arg[0]!='-') {
-        return util::nothing;
+        return util::nullopt;
     }
 
     std::string text;
@@ -91,7 +91,7 @@ util::optional<V> parse_opt(char **& argp, char shortopt, const char* longopt=nu
             argp += 1;
         }
         else {
-            return util::nothing;
+            return util::nullopt;
         }
     }
     else if (shortopt && arg[1]==shortopt && arg[2]==0) {
@@ -100,7 +100,7 @@ util::optional<V> parse_opt(char **& argp, char shortopt, const char* longopt=nu
         argp += 2;
     }
     else {
-        return util::nothing;
+        return util::nullopt;
     }
 
     auto v = parse(text);
@@ -111,7 +111,7 @@ util::optional<V> parse_opt(char **& argp, char shortopt, const char* longopt=nu
 
 util::optional<void> parse_opt(char **& argp, char shortopt, const char* longopt) {
     if (!*argp || *argp[0]!='-') {
-        return util::nothing;
+        return util::nullopt;
     }
     else if (argp[0][1]=='-' && longopt && !std::strcmp(argp[0]+2, longopt)) {
         ++argp;
@@ -122,7 +122,7 @@ util::optional<void> parse_opt(char **& argp, char shortopt, const char* longopt
         return true;
     }
     else {
-        return util::nothing;
+        return util::nullopt;
     }
 }
 
diff --git a/src/util/hostname.cpp b/src/util/hostname.cpp
index 85152e903184253b6e2e959f4f20978d7a72c5e4..a88316d9fd5c2a0f5f1c66fe1da08b092c1f171b 100644
--- a/src/util/hostname.cpp
+++ b/src/util/hostname.cpp
@@ -20,13 +20,13 @@ util::optional<std::string> hostname() {
     char name[256];
     auto result = gethostname(name, sizeof(name));
     if (result) {
-        return util::nothing;
+        return util::nullopt;
     }
     return std::string(name);
 }
 #else
 util::optional<std::string> hostname() {
-    return util::nothing;
+    return util::nullopt;
 }
 #endif
 
diff --git a/src/util/optional.hpp b/src/util/optional.hpp
index f674f8f3a42669c008a6a9b53aa1fc65a7ef863b..6166d8e49c6b5a64dad5ff89ff1131040773b913 100644
--- a/src/util/optional.hpp
+++ b/src/util/optional.hpp
@@ -1,18 +1,39 @@
 #pragma once
 
-/* An option class with a monadic interface.
+/* An option class supporting a subset of C++17 std::optional functionality.
  *
- * 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.
+ * Difference from C++17 std::optional:
  *
- * 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.
+ * Missing functionality (to be added as required):
  *
- * One point of difference between the proposal N3672 and this implementation
- * is the lack of constexpr versions of the methods and constructors.
+ *   1. `constexpr` constructors.
+ *
+ *   2. Comparison operators other than `operator==`.
+ *
+ *   3. `std::hash` overload.
+ *
+ *   4. `swap()` method and ADL-available `swap()` function.
+ *
+ *   5. In-place construction with `std::in_place_t` tags or equivalent.
+ *
+ *   5. No `make_optional` function (but see `just` below).
+ *
+ * Additional/differing functionality:
+ *
+ *   1. Optional references.
+ *
+ *      `util::optional<T&>` acts as a value-like wrapper about a possible
+ *      reference of type T&. Methods such as `value()` or `value_or()`
+ *      return this reference.
+ *
+ *   2. Optional void.
+ *
+ *      Included primarily for ease of generic programming with `optional`.
+ *
+ *   3. `util::just`
+ *
+ *      This function acts like the value-constructing `std::make_optional<T>(T&&)`,
+ *      except that it will return an optional<T&> if given an lvalue T as an argument.
  */
 
 #include <type_traits>
@@ -37,8 +58,8 @@ struct optional_unset_error: std::runtime_error {
     {}
 };
 
-struct nothing_t {};
-constexpr nothing_t nothing{};
+struct nullopt_t {};
+constexpr nullopt_t nullopt{};
 
 namespace detail {
     template <typename Y>
@@ -83,6 +104,8 @@ namespace detail {
 
     protected:
         using data_type = util::uninitialized<X>;
+        using rvalue_reference = typename data_type::rvalue_reference;
+        using const_rvalue_reference = typename data_type::const_rvalue_reference;
 
     public:
         using reference = typename data_type::reference;
@@ -106,6 +129,12 @@ namespace detail {
         reference       ref()       { return data.ref(); }
         const_reference ref() const { return data.cref(); }
 
+        void assert_set() const {
+            if (!set) {
+                throw optional_unset_error();
+            }
+        }
+
     public:
         ~optional_base() {
             if (set) {
@@ -119,20 +148,6 @@ namespace detail {
         reference operator*() { return ref(); }
         const_reference operator*() const { return ref(); }
 
-        reference get() {
-            if (!set) {
-                throw optional_unset_error();
-            }
-            return ref();
-        }
-
-        const_reference get() const {
-            if (!set) {
-                throw optional_unset_error();
-            }
-            return ref();
-        }
-
         explicit operator bool() const { return set; }
 
         template <typename Y>
@@ -151,60 +166,6 @@ namespace detail {
             }
             set = false;
         }
-
-        template <typename F>
-        auto bind(F&& f) -> lift_type_t<decltype(data.apply(std::forward<F>(f)))> {
-            using F_result_type = decltype(data.apply(std::forward<F>(f)));
-            using result_type = lift_type_t<F_result_type>;
-
-            if (!set) {
-                return result_type();
-            }
-
-            return bind_impl<result_type, std::is_void<F_result_type>::value>::
-                       bind(data, std::forward<F>(f));
-        }
-
-        template <typename F>
-        auto bind(F&& f) const -> lift_type_t<decltype(data.apply(std::forward<F>(f)))> {
-            using F_result_type = decltype(data.apply(std::forward<F>(f)));
-            using result_type = lift_type_t<F_result_type>;
-
-            if (!set) {
-                return result_type();
-            }
-
-            return bind_impl<result_type, std::is_void<F_result_type>::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);
-            }
-        };
     };
 
     // type utilities
@@ -227,9 +188,10 @@ struct optional: detail::optional_base<X> {
     using base::ref;
     using base::reset;
     using base::data;
+    using base::assert_set;
 
     optional() noexcept: base() {}
-    optional(nothing_t) noexcept: base() {}
+    optional(nullopt_t) noexcept: base() {}
 
     optional(const X& x)
         noexcept(std::is_nothrow_copy_constructible<X>::value): base(true, x) {}
@@ -250,7 +212,7 @@ struct optional: detail::optional_base<X> {
     optional(optional<T>&& ot)
         noexcept(std::is_nothrow_constructible<X, T&&>::value): base(ot.set, std::move(ot.ref())) {}
 
-    optional& operator=(nothing_t) {
+    optional& operator=(nullopt_t) {
         reset();
         return *this;
     }
@@ -310,6 +272,32 @@ struct optional: detail::optional_base<X> {
         }
         return *this;
     }
+
+    X& value() & {
+        return assert_set(), ref();
+    }
+
+    const X& value() const& {
+        return assert_set(), ref();
+    }
+
+    X&& value() && {
+        return assert_set(), std::move(ref());
+    }
+
+    const X&& value() const&& {
+        return assert_set(), std::move(ref());
+    }
+
+    template <typename T>
+    X value_or(T&& alternative) const& {
+        return set? value(): static_cast<X>(std::forward<T>(alternative));
+    }
+
+    template <typename T>
+    X value_or(T&& alternative) && {
+        return set? std::move(value()): static_cast<X>(std::forward<T>(alternative));
+    }
 };
 
 template <typename X>
@@ -319,15 +307,16 @@ struct optional<X&>: detail::optional_base<X&> {
     using base::ref;
     using base::data;
     using base::reset;
+    using base::assert_set;
 
     optional() noexcept: base() {}
-    optional(nothing_t) noexcept: base() {}
+    optional(nullopt_t) noexcept: base() {}
     optional(X& x) noexcept: base(true, x) {}
 
     template <typename T>
     optional(optional<T&>& ot) noexcept: base(ot.set, ot.ref()) {}
 
-    optional& operator=(nothing_t) {
+    optional& operator=(nullopt_t) {
         reset();
         return *this;
     }
@@ -343,24 +332,39 @@ struct optional<X&>: detail::optional_base<X&> {
     optional& operator=(optional<Y&>& o) {
         set = o.set;
         if (o.set) {
-           data.construct(o.get());
+           data.construct(o.value());
         }
         return *this;
     }
-};
 
+    X& value() {
+        return assert_set(), ref();
+    }
+
+    const X& value() const {
+        return assert_set(), ref();
+    }
+
+    template <typename T>
+    X& value_or(T& alternative) {
+        return set? ref(): static_cast<X&>(alternative);
+    }
 
-/* special case for optional<void>, used as e.g. the result of
- * binding to a void function */
+    template <typename T>
+    const X& value_or(const T& alternative) const {
+        return set? ref(): static_cast<const X&>(alternative);
+    }
+};
 
 template <>
 struct optional<void>: detail::optional_base<void> {
     using base = detail::optional_base<void>;
+    using base::assert_set;
     using base::set;
     using base::reset;
 
     optional(): base() {}
-    optional(nothing_t): base() {}
+    optional(nullopt_t): base() {}
 
     template <typename T>
     optional(T): base(true, true) {}
@@ -368,7 +372,7 @@ struct optional<void>: detail::optional_base<void> {
     template <typename T>
     optional(const optional<T>& o): base(o.set, true) {}
 
-    optional& operator=(nothing_t) {
+    optional& operator=(nullopt_t) {
         reset();
         return *this;
     }
@@ -391,36 +395,12 @@ struct optional<void>: detail::optional_base<void> {
     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<
-            detail::wrapped_type_t<A>,
-            detail::wrapped_type_t<B>
-        >::type
-    >
->::type
-operator|(A&& a, B&& b) {
-    return detail::decay_bool(a) ? a : b;
-}
 
-template <typename A, typename B>
-typename std::enable_if<
-    detail::is_optional<A>::value || detail::is_optional<B>::value,
-    optional<detail::wrapped_type_t<B>>
->::type
-operator&(A&& a, B&& b) {
-    using result_type = optional<detail::wrapped_type_t<B>>;
-    return a ? b: result_type();
-}
+    void value() const { assert_set(); }
 
-inline optional<void> provided(bool condition) {
-    return condition ? optional<void>(true) : optional<void>();
-}
+    template <typename T>
+    void value_or(T) const {} // nop
+};
 
 template <typename X>
 optional<X> just(X&& x) {
diff --git a/src/util/uninitialized.hpp b/src/util/uninitialized.hpp
index b5653939cd8f47ac1a231c48d8b243821eb5a198..a1c42d55da9cac9970105480780e6d916b4ff071 100644
--- a/src/util/uninitialized.hpp
+++ b/src/util/uninitialized.hpp
@@ -32,6 +32,8 @@ public:
     using const_pointer = const X*;
     using reference = X&;
     using const_reference= const X&;
+    using rvalue_reference = X&&;
+    using const_rvalue_reference= const X&&;
 
     pointer ptr() {
         // COMPAT: xlC 13.1.4 workaround:
@@ -92,6 +94,8 @@ public:
     using const_pointer = const X*;
     using reference = X&;
     using const_reference = const X&;
+    using rvalue_reference = X&;
+    using const_rvalue_reference= const X&;
 
     pointer ptr() { return data; }
     const_pointer cptr() const { return data; }
@@ -101,18 +105,6 @@ public:
 
     void construct(X& x) { data = &x; }
     void destruct() {}
-
-    // Apply the one-parameter functor F to the value by reference.
-    template <typename F>
-    result_of_t<F(reference)> apply(F&& f) {
-        return f(ref());
-    }
-
-    // Apply the one-parameter functor F to the value by const reference.
-    template <typename F>
-    result_of_t<F(const_reference)> apply(F&& f) const {
-        return f(cref());
-    }
 };
 
 /* Wrap a void type in an uninitialized template.
@@ -126,6 +118,8 @@ public:
     using const_pointer = const void*;
     using reference = void;
     using const_reference = void;
+    using rvalue_reference = void;
+    using const_rvalue_reference = void;
 
     pointer ptr() { return nullptr; }
     const_pointer cptr() const { return nullptr; }
@@ -137,10 +131,6 @@ public:
     void construct(...) {}
     // No operation.
     void destruct() {}
-
-    // Equivalent to f()
-    template <typename F>
-    result_of_t<F()> apply(F&& f) const { return f(); }
 };
 
 } // namespace util
diff --git a/tests/unit/common.hpp b/tests/unit/common.hpp
index 445f59928abe7f5d74c2f77b26422b71320bb1af..70eace9a79c0500ceddf4658b0eae5f115c7ee39 100644
--- a/tests/unit/common.hpp
+++ b/tests/unit/common.hpp
@@ -5,13 +5,22 @@
  * more than one unit test.
  */
 
-#include <utility>
 #include <cmath>
+#include <string>
+#include <utility>
 
 #include "../gtest.h"
 
 namespace testing {
 
+// string ctor suffix (until C++14!)
+
+namespace string_literals {
+    inline std::string operator ""_s(const char* s, std::size_t n) {
+        return std::string(s, n);
+    }
+}
+
 // sentinel for use with range-related tests
 
 struct null_terminated_t {
diff --git a/tests/unit/test_any.cpp b/tests/unit/test_any.cpp
index 12aa3598258b1a6960065aa06166ae0e7f48a82f..cce483f67f025753b2815ced083c6f076b305a7d 100644
--- a/tests/unit/test_any.cpp
+++ b/tests/unit/test_any.cpp
@@ -10,6 +10,7 @@
 #include <typeinfo>
 
 using namespace arb;
+using namespace testing::string_literals;
 
 TEST(any, copy_construction) {
     util::any any_int(2);
@@ -75,7 +76,7 @@ TEST(any, type) {
     using util::any;
 
     any anyi(42);
-    any anys(std::string("hello"));
+    any anys("hello"_s);
     any anyv(std::vector<int>{1, 2, 3});
     any any0;
 
@@ -141,7 +142,7 @@ TEST(any, any_cast_ptr) {
     auto ptr_i = util::any_cast<int>(&ai);
     EXPECT_EQ(*ptr_i, 42);
 
-    util::any as(std::string("hello"));
+    util::any as("hello"_s);
     auto ptr_s = util::any_cast<std::string>(&as);
     EXPECT_EQ(*ptr_s, "hello");
 
@@ -303,7 +304,7 @@ TEST(any, make_any) {
         // create a string from const char*
         auto a = make_any<std::string>("hello");
 
-        EXPECT_EQ(any_cast<std::string>(a), std::string("hello"));
+        EXPECT_EQ(any_cast<std::string>(a), "hello"_s);
     }
 
     // test that we make_any correctly forwards rvalue arguments to the constructor
diff --git a/tests/unit/test_fvm_multi.cpp b/tests/unit/test_fvm_multi.cpp
index 1d2f940858be2ffeffb3b69d88947258c0438dec..aa65c7bbf438ff56d690da1c57b680b4e2ba835c 100644
--- a/tests/unit/test_fvm_multi.cpp
+++ b/tests/unit/test_fvm_multi.cpp
@@ -57,7 +57,7 @@ TEST(fvm_multi, init)
     const auto m = cell.model();
     EXPECT_EQ(m.tree.num_segments(), 2u);
 
-    auto& soma_hh = (cell.soma()->mechanism("hh")).get();
+    auto& soma_hh = (cell.soma()->mechanism("hh")).value();
 
     soma_hh.set("gnabar", 0.12);
     soma_hh.set("gkbar", 0.036);
@@ -200,7 +200,7 @@ TEST(fvm_multi, stimulus)
     auto ref = fvcell.find_mechanism("stimulus");
     ASSERT_TRUE(ref) << "no stimuli retrieved from lowered fvm cell: expected 2";
 
-    auto& stims = ref.get();
+    auto& stims = ref.value();
     EXPECT_EQ(stims->size(), 2u);
 
     auto I = fvcell.current();
diff --git a/tests/unit/test_optional.cpp b/tests/unit/test_optional.cpp
index 73384f1b4d009643c0ebc103b98dc5c75c8bfc33..d19fbbc2720fb32ef1bd0caee81100b0deb0eeba 100644
--- a/tests/unit/test_optional.cpp
+++ b/tests/unit/test_optional.cpp
@@ -1,114 +1,116 @@
-#include <typeinfo>
-#include <array>
 #include <algorithm>
+#include <array>
+#include <string>
+#include <typeinfo>
 
 #include "../gtest.h"
 #include "util/optional.hpp"
 #include "common.hpp"
 
 using namespace arb::util;
+using namespace testing::string_literals;
 
-TEST(optionalm,ctors) {
-    optional<int> a,b(3),c=b,d=4;
+TEST(optional, 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());
+    EXPECT_EQ(3, b.value());
+    EXPECT_EQ(3, c.value());
+    EXPECT_EQ(4, d.value());
 }
 
-TEST(optionalm,unset_throw) {
+TEST(optional, unset_throw) {
     optional<int> a;
-    int check=10;
+    int check = 10;
 
     try {
-        a.get();
+        a.value();
     }
     catch (optional_unset_error& e) {
         ++check;
     }
-    EXPECT_EQ(11,check);
+    EXPECT_EQ(11, check);
 
-    check=20;
-    a=2;
+    check = 20;
+    a = 2;
     try {
-        a.get();
+        a.value();
     }
     catch (optional_unset_error& e) {
         ++check;
     }
-    EXPECT_EQ(20,check);
+    EXPECT_EQ(20, check);
 
-    check=30;
+    check = 30;
     a.reset();
     try {
-        a.get();
+        a.value();
     }
     catch (optional_unset_error& e) {
         ++check;
     }
-    EXPECT_EQ(31,check);
+    EXPECT_EQ(31, check);
 }
 
-TEST(optionalm,deref) {
+TEST(optional, 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);
+    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(3u,x->size());
+TEST(optional, ctor_conv) {
+    optional<std::array<int, 3>> x{{1, 2, 3}};
+    EXPECT_EQ(3u, x->size());
 }
 
-TEST(optionalm,ctor_ref) {
-    int v=10;
+TEST(optional, ctor_ref) {
+    int v = 10;
     optional<int&> a(v);
 
-    EXPECT_EQ(10,a.get());
-    v=20;
-    EXPECT_EQ(20,a.get());
+    EXPECT_EQ(10, a.value());
+    v = 20;
+    EXPECT_EQ(20, a.value());
 
-    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()));
+    optional<int&> b(a), c = b, d = v;
+    EXPECT_EQ(&(a.value()), &(b.value()));
+    EXPECT_EQ(&(a.value()), &(c.value()));
+    EXPECT_EQ(&(a.value()), &(d.value()));
 }
 
-TEST(optionalm,assign_returns) {
-    optional<int> a=3;
+TEST(optional, assign_returns) {
+    optional<int> a = 3;
 
-    auto b=(a=4);
-    EXPECT_EQ(typeid(optional<int>),typeid(b));
+    auto b = (a = 4);
+    EXPECT_EQ(typeid(optional<int>), typeid(b));
 
-    auto bp=&(a=4);
-    EXPECT_EQ(&a,bp);
+    auto bp = &(a = 4);
+    EXPECT_EQ(&a, bp);
 
-    auto b2=(a=optional<int>(10));
-    EXPECT_EQ(typeid(optional<int>),typeid(b2));
+    auto b2 = (a = optional<int>(10));
+    EXPECT_EQ(typeid(optional<int>), typeid(b2));
 
-    auto bp2=&(a=4);
-    EXPECT_EQ(&a,bp2);
+    auto bp2 = &(a = 4);
+    EXPECT_EQ(&a, bp2);
 
-    auto b3=(a=nothing);
-    EXPECT_EQ(typeid(optional<int>),typeid(b3));
+    auto b3 = (a = nullopt);
+    EXPECT_EQ(typeid(optional<int>), typeid(b3));
 
-    auto bp3=&(a=4);
-    EXPECT_EQ(&a,bp3);
+    auto bp3 = &(a = 4);
+    EXPECT_EQ(&a, bp3);
 }
 
-TEST(optionalm,assign_reference) {
-    double a=3.0;
+TEST(optional, assign_reference) {
+    double a = 3.0;
     optional<double&> ar;
     optional<double&> br;
 
@@ -117,234 +119,169 @@ TEST(optionalm,assign_reference) {
     *ar = 5.0;
     EXPECT_EQ(5.0, a);
 
-    auto& check_rval=(br=ar);
+    auto& check_rval = (br = ar);
     EXPECT_TRUE(br);
     EXPECT_EQ(&br, &check_rval);
 
     *br = 7.0;
     EXPECT_EQ(7.0, a);
 
-    auto& check_rval2=(br=nothing);
+    auto& check_rval2 = (br = nullopt);
     EXPECT_FALSE(br);
     EXPECT_EQ(&br, &check_rval2);
 }
 
-TEST(optionalm,ctor_nomove) {
+TEST(optional, ctor_nomove) {
     using nomove = testing::nomove<int>;
 
     optional<nomove> a(nomove(3));
-    EXPECT_EQ(nomove(3),a.get());
+    EXPECT_EQ(nomove(3), a.value());
 
     optional<nomove> b;
-    b=a;
-    EXPECT_EQ(nomove(3),b.get());
+    b = a;
+    EXPECT_EQ(nomove(3), b.value());
 
-    b=optional<nomove>(nomove(4));
-    EXPECT_EQ(nomove(4),b.get());
+    b = optional<nomove>(nomove(4));
+    EXPECT_EQ(nomove(4), b.value());
 }
 
-TEST(optionalm,ctor_nocopy) {
+TEST(optional, ctor_nocopy) {
     using nocopy = testing::nocopy<int>;
 
     optional<nocopy> a(nocopy(5));
-    EXPECT_EQ(nocopy(5),a.get());
+    EXPECT_EQ(nocopy(5), a.value());
 
     nocopy::reset_counts();
     optional<nocopy> b(std::move(a));
-    EXPECT_EQ(nocopy(5),b.get());
-    EXPECT_EQ(0,a.get().value);
+    EXPECT_EQ(nocopy(5), b.value());
+    EXPECT_EQ(0, a.value().value);
     EXPECT_EQ(1, nocopy::move_ctor_count);
     EXPECT_EQ(0, nocopy::move_assign_count);
 
     nocopy::reset_counts();
-    b=optional<nocopy>(nocopy(6));
-    EXPECT_EQ(nocopy(6),b.get());
+    b = optional<nocopy>(nocopy(6));
+    EXPECT_EQ(nocopy(6), b.value());
     EXPECT_EQ(1, nocopy::move_ctor_count);
     EXPECT_EQ(1, nocopy::move_assign_count);
 
-}
+    nocopy::reset_counts();
+    nocopy v = optional<nocopy>(nocopy(9)).value();
+    EXPECT_EQ(2, nocopy::move_ctor_count);
+    EXPECT_EQ(nocopy(9), v.value);
 
-static optional<double> odd_half(int n) {
-    optional<double> h;
-    if (n%2==1) h=n/2.0;
-    return h;
+    const optional<nocopy> ccheck(nocopy(1));
+    EXPECT_TRUE(std::is_rvalue_reference<decltype(std::move(ccheck).value())>::value);
+    EXPECT_TRUE(std::is_const<std::remove_reference<decltype(std::move(ccheck).value())>::type>::value);
 }
 
-TEST(optionalm,bind) {
-    optional<int> a;
-    auto b=a.bind(odd_half);
-
-    EXPECT_EQ(typeid(optional<double>),typeid(b));
+TEST(optional, value_or) {
+    optional<double> x = 3;
+    EXPECT_EQ(3., x.value_or(5));
 
-    a=10;
-    b=a.bind(odd_half);
-    EXPECT_FALSE((bool)b);
+    x = nullopt;
+    EXPECT_EQ(5., x.value_or(5));
 
-    a=11;
-    b=a.bind(odd_half);
-    EXPECT_TRUE((bool)b);
-    EXPECT_EQ(5.5,b.get());
+    // `value_or` returns T for optional<T>:
+    struct check_conv {
+        bool value = false;
+        explicit check_conv(bool value): value(value) {}
 
-    b=a >> odd_half >> [](double x) { return (int)x; } >> odd_half;
-    EXPECT_TRUE((bool)b);
-    EXPECT_EQ(2.5,b.get());
-}
+        explicit operator std::string() const {
+            return value? "true": "false";
+        }
+    };
+    check_conv cc{true};
 
-TEST(optionalm,void) {
-    optional<void> a,b(true),c(a),d=b,e(false),f(nothing);
+    optional<std::string> present = "present"_s;
+    optional<std::string> absent; // nullopt
 
-    EXPECT_FALSE((bool)a);
-    EXPECT_TRUE((bool)b);
-    EXPECT_FALSE((bool)c);
-    EXPECT_TRUE((bool)d);
-    EXPECT_TRUE((bool)e);
-    EXPECT_FALSE((bool)f);
+    auto result = present.value_or(cc);
+    EXPECT_EQ(typeid(std::string), typeid(result));
+    EXPECT_EQ("present"_s, result);
 
-    auto x=a >> []() { return 1; };
-    EXPECT_FALSE((bool)x);
+    result = absent.value_or(cc);
+    EXPECT_EQ("true"_s, result);
 
-    x=b >> []() { return 1; };
-    EXPECT_TRUE((bool)x);
-    EXPECT_EQ(1,x.get());
+    // Check move semantics in argument:
 
-    auto& check_rval=(b=nothing);
-    EXPECT_FALSE((bool)b);
-    EXPECT_EQ(&b,&check_rval);
-}
+    using nocopy = testing::nocopy<int>;
 
-TEST(optionalm,bind_to_void) {
-    optional<int> a,b(3);
+    nocopy::reset_counts();
+    nocopy z1 = optional<nocopy>().value_or(nocopy(7));
 
-    int call_count=0;
-    auto vf=[&call_count](int i) -> void { ++call_count; };
+    EXPECT_EQ(7, z1.value);
+    EXPECT_EQ(1, nocopy::move_ctor_count);
 
-    auto x=a >> vf;
-    EXPECT_EQ(typeid(optional<void>),typeid(x));
-    EXPECT_FALSE((bool)x);
-    EXPECT_EQ(0,call_count);
+    nocopy::reset_counts();
+    nocopy z2 = optional<nocopy>(nocopy(3)).value_or(nocopy(7));
 
-    call_count=0;
-    x=b >> vf;
-    EXPECT_TRUE((bool)x);
-    EXPECT_EQ(1,call_count);
+    EXPECT_EQ(3, z2.value);
+    EXPECT_EQ(2, nocopy::move_ctor_count);
 }
 
-TEST(optionalm,bind_to_optional_void) {
-    optional<int> a,b(3),c(4);
+TEST(optional, ref_value_or) {
+    double a = 2.0;
+    double b = 3.0;
 
-    int count=0;
-    auto count_if_odd=[&count](int i) {
-        return i%2?(++count,optional<void>(true)):optional<void>();
-    };
+    optional<double&> x = a;
+    double& ref1 = x.value_or(b);
 
-    auto x=a >> count_if_odd;
-    EXPECT_EQ(typeid(optional<void>),typeid(x));
-    EXPECT_FALSE((bool)x);
-    EXPECT_EQ(0,count);
+    EXPECT_EQ(2., ref1);
 
-    count=0;
-    x=b >> count_if_odd;
-    EXPECT_TRUE((bool)x);
-    EXPECT_EQ(1,count);
+    x = nullopt;
+    double& ref2 = x.value_or(b);
 
-    count=0;
-    x=c >> count_if_odd;
-    EXPECT_FALSE((bool)x);
-    EXPECT_EQ(0,count);
-}
+    EXPECT_EQ(3., ref2);
+
+    ref1 = 12.;
+    ref2 = 13.;
+    EXPECT_EQ(12., a);
+    EXPECT_EQ(13., b);
 
-TEST(optionalm,bind_with_ref) {
-    optional<int> a=10;
-    a >> [](int& v) { ++v; };
-    EXPECT_EQ(11,*a);
+    const optional<double&> cx = x;
+    auto& ref3 = cx.value_or(b);
+    EXPECT_TRUE(std::is_const<std::remove_reference<decltype(ref3)>::type>::value);
+    EXPECT_EQ(&b, &ref3);
 }
 
-struct check_cref {
-    int operator()(const int&) { return 10; }
-    int operator()(int&) { return 11; }
-};
+TEST(optional, void) {
+    optional<void> a, b(true), c(a), d = b, e(false), f(nullopt);
 
-TEST(optionalm,bind_constness) {
-    check_cref checker;
-    optional<int> a=1;
-    int v=*(a >> checker);
-    EXPECT_EQ(11,v);
+    EXPECT_FALSE((bool)a);
+    EXPECT_TRUE((bool)b);
+    EXPECT_FALSE((bool)c);
+    EXPECT_TRUE((bool)d);
+    EXPECT_TRUE((bool)e);
+    EXPECT_FALSE((bool)f);
 
-    const optional<int> b=1;
-    v=*(b >> checker);
-    EXPECT_EQ(10,v);
+    auto& check_rval = (b = nullopt);
+    EXPECT_FALSE((bool)b);
+    EXPECT_EQ(&b, &check_rval);
 }
 
-
-TEST(optionalm,conversion) {
-    optional<double> a(3),b=5;
+TEST(optional, 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());
+    EXPECT_EQ(3.0, a.value());
+    EXPECT_EQ(5.0, b.value());
 
     optional<int> x;
     optional<double> c(x);
-    optional<double> d=optional<int>();
+    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=(char *)0 | 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;
+TEST(optional, just) {
+    int x = 3;
 
-    std::transform(ps.begin(),ps.end(),qs.begin(),rs.begin(),
-        [](int p,int q) { return *( provided(q!=0) >> [=]() { return p/q; } | -1 ); });
+    optional<int&> o1 = just(x);
+    optional<int>  o2 = just(x);
 
-    EXPECT_EQ(14,rs[0]);
-    EXPECT_EQ(-1,rs[1]);
-    EXPECT_EQ(4,rs[2]);
+    o1.value() = 4;
+    optional<int>  o3 = just(x);
+    EXPECT_EQ(4, o1.value());
+    EXPECT_EQ(3, o2.value());
+    EXPECT_EQ(4, o3.value());
 }
diff --git a/tests/unit/test_range.cpp b/tests/unit/test_range.cpp
index fb9012d3cbacd4805aae69a223f56f4c6e241a9b..09e22c43ebd6d961b323065999951d267df611f2 100644
--- a/tests/unit/test_range.cpp
+++ b/tests/unit/test_range.cpp
@@ -23,6 +23,8 @@
 #include "common.hpp"
 
 using namespace arb;
+
+using namespace  testing::string_literals;
 using testing::null_terminated;
 using testing::nocopy;
 using testing::nomove;
@@ -418,11 +420,11 @@ TEST(range, sort) {
 
     // simple sort
     util::sort(util::strict_view(cstr_range));
-    EXPECT_EQ(std::string("dhowy"), cstr);
+    EXPECT_EQ("dhowy"_s, cstr);
 
     // reverse sort by transform c to -c
     util::sort_by(util::strict_view(cstr_range), [](char c) { return -c; });
-    EXPECT_EQ(std::string("ywohd"), cstr);
+    EXPECT_EQ("ywohd"_s, cstr);
 
     // stable sort: move capitals to front, numbers to back
     auto rank = [](char c) {
@@ -433,7 +435,7 @@ TEST(range, sort) {
     auto mixed_range = util::make_range(std::begin(mixed), null_terminated);
 
     util::stable_sort_by(util::strict_view(mixed_range), rank);
-    EXPECT_EQ(std::string("HELLOthere54321"), mixed);
+    EXPECT_EQ("HELLOthere54321"_s, mixed);
 
 
     // sort with user-provided less comparison function
@@ -453,7 +455,7 @@ TEST(range, sum_by) {
     auto result = util::sum_by(words, prepend_);
     EXPECT_EQ("_fish_cakes_!", result);
 
-    result = util::sum_by(words, prepend_, std::string("tasty"));
+    result = util::sum_by(words, prepend_, "tasty"_s);
     EXPECT_EQ("tasty_fish_cakes_!", result);
 
     auto count = util::sum_by(words, [](const std::string &x) { return x.size(); });
@@ -475,10 +477,10 @@ TEST(range, all_of_any_of) {
     auto pred = [](char c) { return c=='x'? throw c:c<'5'; };
 
     // all
-    EXPECT_TRUE(util::all_of(std::string(), pred));
-    EXPECT_TRUE(util::all_of(std::string("1234"), pred));
-    EXPECT_FALSE(util::all_of(std::string("12345"), pred));
-    EXPECT_FALSE(util::all_of(std::string("12345x"), pred));
+    EXPECT_TRUE(util::all_of(""_s, pred));
+    EXPECT_TRUE(util::all_of("1234"_s, pred));
+    EXPECT_FALSE(util::all_of("12345"_s, pred));
+    EXPECT_FALSE(util::all_of("12345x"_s, pred));
 
     EXPECT_TRUE(util::all_of(cstr(""), pred));
     EXPECT_TRUE(util::all_of(cstr("1234"), pred));
@@ -486,10 +488,10 @@ TEST(range, all_of_any_of) {
     EXPECT_FALSE(util::all_of(cstr("12345x"), pred));
 
     // any
-    EXPECT_FALSE(util::any_of(std::string(), pred));
-    EXPECT_FALSE(util::any_of(std::string("8765"), pred));
-    EXPECT_TRUE(util::any_of(std::string("87654"), pred));
-    EXPECT_TRUE(util::any_of(std::string("87654x"), pred));
+    EXPECT_FALSE(util::any_of(""_s, pred));
+    EXPECT_FALSE(util::any_of("8765"_s, pred));
+    EXPECT_TRUE(util::any_of("87654"_s, pred));
+    EXPECT_TRUE(util::any_of("87654x"_s, pred));
 
     EXPECT_FALSE(util::any_of(cstr(""), pred));
     EXPECT_FALSE(util::any_of(cstr("8765"), pred));
diff --git a/tests/unit/test_strprintf.cpp b/tests/unit/test_strprintf.cpp
index 93eacb40ee1fb4d1bfa8f8c1e0466c1c4f5a51ce..3bb01dd8f409f2737d2514458e8b74ca44f46970 100644
--- a/tests/unit/test_strprintf.cpp
+++ b/tests/unit/test_strprintf.cpp
@@ -2,11 +2,13 @@
 #include <memory>
 #include <string>
 
-#include "../gtest.h"
 #include <util/strprintf.hpp>
 
-using namespace arb::util;
+#include "../gtest.h"
+#include "common.hpp"
 
+using namespace arb::util;
+using namespace testing::string_literals;
 
 TEST(strprintf, simple) {
     char buf[200];
@@ -58,6 +60,6 @@ TEST(strprintf, wrappers) {
 
     EXPECT_EQ(std::string(buf), strprintf("sptr %p", sptr));
 
-    EXPECT_EQ(std::string("fish"), strprintf("fi%s", std::string("sh")));
+    EXPECT_EQ("fish"_s, strprintf("fi%s", "sh"_s));
 }
 
diff --git a/tests/unit/test_uninitialized.cpp b/tests/unit/test_uninitialized.cpp
index bb38e3d038ff1c2656ae8442183987029d9d85c2..59683b2e11138fb15a74ee97c14c084953c6f289 100644
--- a/tests/unit/test_uninitialized.cpp
+++ b/tests/unit/test_uninitialized.cpp
@@ -14,21 +14,21 @@ namespace {
         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 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;
+            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;
+    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) {
+TEST(uninitialized, ctor) {
     count_ops::reset_counts();
 
     uninitialized<count_ops> ua;
@@ -37,37 +37,37 @@ TEST(uninitialized,ctor) {
     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);
+    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;
+    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);
+    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);
 }
 
-TEST(uninitialized,ctor_nocopy) {
+TEST(uninitialized, ctor_nocopy) {
     using nocopy = testing::nocopy<int>;
     nocopy::reset_counts();
 
     uninitialized<nocopy> ua;
     ua.construct(nocopy{});
 
-    EXPECT_EQ(1,nocopy::move_ctor_count);
-    EXPECT_EQ(0,nocopy::move_assign_count);
+    EXPECT_EQ(1, nocopy::move_ctor_count);
+    EXPECT_EQ(0, nocopy::move_assign_count);
 
-    ua.ref()=nocopy{};
+    ua.ref() = nocopy{};
 
-    EXPECT_EQ(1,nocopy::move_ctor_count);
-    EXPECT_EQ(1,nocopy::move_assign_count);
+    EXPECT_EQ(1, nocopy::move_ctor_count);
+    EXPECT_EQ(1, nocopy::move_assign_count);
 }
 
-TEST(uninitialized,ctor_nomove) {
+TEST(uninitialized, ctor_nomove) {
     using nomove = testing::nomove<int>;
     nomove::reset_counts();
 
@@ -80,91 +80,37 @@ TEST(uninitialized,ctor_nomove) {
     const nomove c;
     ua.construct(c); // check against const lvalue
 
-    EXPECT_EQ(3,nomove::copy_ctor_count);
-    EXPECT_EQ(0,nomove::copy_assign_count);
+    EXPECT_EQ(3, nomove::copy_ctor_count);
+    EXPECT_EQ(0, nomove::copy_assign_count);
 
     nomove a;
-    ua.ref()=a;
+    ua.ref() = a;
 
-    EXPECT_EQ(3,nomove::copy_ctor_count);
-    EXPECT_EQ(1,nomove::copy_assign_count);
+    EXPECT_EQ(3, nomove::copy_ctor_count);
+    EXPECT_EQ(1, nomove::copy_assign_count);
 }
 
-TEST(uninitialized,void) {
-    uninitialized<void> a,b;
-    a=b;
+TEST(uninitialized, void) {
+    uninitialized<void> a, b;
+    a = b;
 
-    EXPECT_EQ(typeid(a.ref()),typeid(void));
+    EXPECT_EQ(typeid(a.ref()), typeid(void));
 }
 
-TEST(uninitialized,ref) {
-    uninitialized<int&> x,y;
+TEST(uninitialized, ref) {
+    uninitialized<int&> x, y;
     int a;
 
     x.construct(a);
-    y=x;
+    y = x;
 
-    x.ref()=2;
-    EXPECT_EQ(2,a);
+    x.ref() = 2;
+    EXPECT_EQ(2, a);
 
-    y.ref()=3;
-    EXPECT_EQ(3,a);
-    EXPECT_EQ(3,x.cref());
+    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; }));
+    EXPECT_EQ(&a, x.ptr());
+    EXPECT_EQ((const int *)&a, x.cptr());
 }
diff --git a/tests/unit/test_unique_any.cpp b/tests/unit/test_unique_any.cpp
index 8fc88c1e9f453a3fb6f973a848e10ecfa25e0fed..0104619a73ed623cb8609ca7b98947c30cbc0cb7 100644
--- a/tests/unit/test_unique_any.cpp
+++ b/tests/unit/test_unique_any.cpp
@@ -1,13 +1,14 @@
-#include "../gtest.h"
-#include "common.hpp"
-
 #include <iostream>
 
 #include <util/rangeutil.hpp>
 #include <util/span.hpp>
 #include <util/unique_any.hpp>
 
+#include "../gtest.h"
+#include "common.hpp"
+
 using namespace arb;
+using namespace testing::string_literals;
 
 TEST(unique_any, copy_construction) {
     using util::unique_any;
@@ -24,7 +25,6 @@ TEST(unique_any, copy_construction) {
 }
 
 namespace {
-
     struct moveable {
         moveable() = default;
 
@@ -39,7 +39,6 @@ namespace {
         int moves=0;
         int copies=0;
     };
-
 }
 
 TEST(unique_any, move_construction) {
diff --git a/tests/validation/trace_analysis.cpp b/tests/validation/trace_analysis.cpp
index 28d9a47055e714319d7fcc54979d58b3d3293697..685699835a851e15c668cccb4ef7f3a695f1fe90 100644
--- a/tests/validation/trace_analysis.cpp
+++ b/tests/validation/trace_analysis.cpp
@@ -119,7 +119,7 @@ util::optional<trace_peak> peak_delta(const trace_data<double>& a, const trace_d
     auto p = local_maxima(a);
     auto q = local_maxima(b);
 
-    if (p.size()!=q.size() || p.empty()) return util::nothing;
+    if (p.size()!=q.size() || p.empty()) return util::nullopt;
 
     auto max_delta = p[0]-q[0];