diff --git a/arbor/cable_cell_param.cpp b/arbor/cable_cell_param.cpp
index bc7e26152b127f40b26f15514b5669fc36c6ea4c..d1e491ece8534bc0fa314b78950ddfa23a2042a8 100644
--- a/arbor/cable_cell_param.cpp
+++ b/arbor/cable_cell_param.cpp
@@ -50,10 +50,6 @@ void check_global_properties(const cable_cell_global_properties& G) {
         }
     }
 }
-    util::optional<double> init_membrane_potential; // [mV]
-    util::optional<double> temperature_K;           // [K]
-    util::optional<double> axial_resistivity;       // [Ω·cm]
-    util::optional<double> membrane_capacitance;    // [F/m²]
 
 cable_cell_parameter_set neuron_parameter_defaults = {
     // initial membrane potential [mV]
@@ -108,7 +104,7 @@ locset cv_policy_max_extent::cv_boundary_points(const cable_cell& cell) const {
         ++bidx;
     }
 
-    return std::accumulate(points.begin(), points.end(), ls::nil(), [](auto& l, auto& p) { return join(l, ls::location(p)); });
+    return points;
 }
 
 locset cv_policy_fixed_per_branch::cv_boundary_points(const cable_cell& cell) const {
@@ -140,7 +136,7 @@ locset cv_policy_fixed_per_branch::cv_boundary_points(const cable_cell& cell) co
         ++bidx;
     }
 
-    return std::accumulate(points.begin(), points.end(), ls::nil(), [](auto& l, auto& p) { return join(l, ls::location(p)); });
+    return points;
 }
 
 } // namespace arb
diff --git a/arbor/include/arbor/morph/locset.hpp b/arbor/include/arbor/morph/locset.hpp
index 1ca7e066f8bb5a294971ecd94675d30e5246a87b..da2688ec57e41234d9d0ac08c6515842caa7fa6f 100644
--- a/arbor/include/arbor/morph/locset.hpp
+++ b/arbor/include/arbor/morph/locset.hpp
@@ -16,11 +16,12 @@ namespace arb {
 struct mprovider;
 
 class locset;
+class locset_tag {};
 
 class locset {
 public:
     template <typename Impl,
-              typename X=std::enable_if_t<!std::is_same<std::decay_t<Impl>, locset>::value>>
+              typename = std::enable_if_t<std::is_base_of<locset_tag, std::decay_t<Impl>>::value>>
     explicit locset(Impl&& impl):
         impl_(new wrap<Impl>(std::forward<Impl>(impl))) {}
 
@@ -41,15 +42,16 @@ public:
     // The default constructor creates an empty "nil" set.
     locset();
 
-    // Construct an explicit location set with a single location.
+    // Implicity convert mlocation and mlocation_lists to locsets.
     locset(mlocation other);
+    locset(const mlocation_list& other);
 
     // Implicitly convert string to named locset expression.
     locset(std::string label);
     locset(const char* label);
 
     template <typename Impl,
-              typename X=std::enable_if_t<!std::is_same<std::decay_t<Impl>, locset>::value>>
+              typename = std::enable_if_t<std::is_base_of<locset_tag, std::decay_t<Impl>>::value>>
     locset& operator=(Impl&& other) {
         impl_ = new wrap<Impl>(std::forward<Impl>(other));
         return *this;
@@ -119,7 +121,7 @@ private:
 namespace ls {
 
 // Explicit location on morphology.
-locset location(mlocation);
+locset location(msize_t branch, double pos);
 
 // Location of a sample.
 locset sample(msize_t);
diff --git a/arbor/include/arbor/morph/region.hpp b/arbor/include/arbor/morph/region.hpp
index 9173bd27b1450c59e9203a033c53a4dd54a18c7c..bd049fd68225f314ca2d92e1cf1e0bce33a774d1 100644
--- a/arbor/include/arbor/morph/region.hpp
+++ b/arbor/include/arbor/morph/region.hpp
@@ -14,11 +14,12 @@
 namespace arb {
 
 struct mprovider;
+struct region_tag {};
 
 class region {
 public:
     template <typename Impl,
-              typename X=std::enable_if_t<!std::is_same<std::decay_t<Impl>, region>::value>>
+              typename = std::enable_if_t<std::is_base_of<region_tag, std::decay_t<Impl>>::value>>
     explicit region(Impl&& impl):
         impl_(new wrap<Impl>(std::forward<Impl>(impl))) {}
 
@@ -40,7 +41,7 @@ public:
     }
 
     template <typename Impl,
-              typename X=std::enable_if_t<!std::is_same<std::decay_t<Impl>, region>::value>>
+              typename = std::enable_if_t<std::is_base_of<region_tag, std::decay_t<Impl>>::value>>
     region& operator=(Impl&& other) {
         impl_ = new wrap<Impl>(std::forward<Impl>(other));
         return *this;
@@ -52,6 +53,10 @@ public:
         return *this;
     }
 
+    // Implicit conversion from mcable or mcable_list.
+    region(mcable);
+    region(const mcable_list&);
+
     // Implicitly convert string to named region expression.
     region(std::string label);
     region(const char* label);
@@ -133,9 +138,10 @@ region named(std::string);
 
 } // namespace reg
 
-// union of two regions
+// Union of two regions.
 region join(region, region);
-// intersection of two regions
+
+// Intersection of two regions.
 region intersect(region, region);
 
 } // namespace arb
diff --git a/arbor/morph/locset.cpp b/arbor/morph/locset.cpp
index 3fa6ea893bd1f9ee7b42e004d9a1d1ed1e75c0ee..715ddcc0e3b3dcb866cde30dcd2ddd53e282c3e6 100644
--- a/arbor/morph/locset.cpp
+++ b/arbor/morph/locset.cpp
@@ -23,7 +23,7 @@ void assert_valid(mlocation x) {
 
 // Empty locset.
 
-struct nil_ {};
+struct nil_: locset_tag {};
 
 locset nil() {
     return locset{nil_{}};
@@ -39,11 +39,13 @@ std::ostream& operator<<(std::ostream& o, const nil_& x) {
 
 // An explicit location.
 
-struct location_ {
+struct location_: locset_tag {
+    explicit location_(mlocation loc): loc(loc) {}
     mlocation loc;
 };
 
-locset location(mlocation loc) {
+locset location(msize_t branch, double pos) {
+    mlocation loc{branch, pos};
     assert_valid(loc);
     return locset{location_{loc}};
 }
@@ -63,7 +65,8 @@ std::ostream& operator<<(std::ostream& o, const location_& x) {
 
 // Location corresponding to a sample id.
 
-struct sample_ {
+struct sample_: locset_tag {
+    explicit sample_(msize_t index): index(index) {}
     msize_t index;
 };
 
@@ -81,7 +84,7 @@ std::ostream& operator<<(std::ostream& o, const sample_& x) {
 
 // Set of terminal points (most distal points).
 
-struct terminal_ {};
+struct terminal_: locset_tag {};
 
 locset terminal() {
     return locset{terminal_{}};
@@ -101,7 +104,7 @@ std::ostream& operator<<(std::ostream& o, const terminal_& x) {
 
 // Root location (most proximal point).
 
-struct root_ {};
+struct root_: locset_tag {};
 
 locset root() {
     return locset{root_{}};
@@ -117,7 +120,8 @@ std::ostream& operator<<(std::ostream& o, const root_& x) {
 
 // Named locset.
 
-struct named_ {
+struct named_: locset_tag {
+    explicit named_(std::string name): name(std::move(name)) {}
     std::string name;
 };
 
@@ -136,7 +140,7 @@ std::ostream& operator<<(std::ostream& o, const named_& x) {
 
 // Intersection of two point sets.
 
-struct land {
+struct land: locset_tag {
     locset lhs;
     locset rhs;
     land(locset lhs, locset rhs): lhs(std::move(lhs)), rhs(std::move(rhs)) {}
@@ -152,7 +156,7 @@ std::ostream& operator<<(std::ostream& o, const land& x) {
 
 // Union of two point sets.
 
-struct lor {
+struct lor: locset_tag {
     locset lhs;
     locset rhs;
     lor(locset lhs, locset rhs): lhs(std::move(lhs)), rhs(std::move(rhs)) {}
@@ -168,7 +172,7 @@ std::ostream& operator<<(std::ostream& o, const lor& x) {
 
 // Sum of two point sets.
 
-struct lsum {
+struct lsum: locset_tag {
     locset lhs;
     locset rhs;
     lsum(locset lhs, locset rhs): lhs(std::move(lhs)), rhs(std::move(rhs)) {}
@@ -200,12 +204,19 @@ locset sum(locset lhs, locset rhs) {
     return locset(ls::lsum(std::move(lhs), std::move(rhs)));
 }
 
+// Implicit constructors.
+
 locset::locset() {
     *this = ls::nil();
 }
 
 locset::locset(mlocation loc) {
-    *this = ls::location(loc);
+    *this = ls::location(loc.branch, loc.pos);
+}
+
+locset::locset(const mlocation_list& ll) {
+    *this = std::accumulate(ll.begin(), ll.end(), ls::nil(),
+        [](auto& ls, auto& p) { return sum(ls, locset(p)); });
 }
 
 locset::locset(std::string name) {
@@ -216,5 +227,4 @@ locset::locset(const char* name) {
     *this = ls::named(name);
 }
 
-
 } // namespace arb
diff --git a/arbor/morph/region.cpp b/arbor/morph/region.cpp
index cb68726f69ddccd16a7f59c8717e465d3cc4ba89..3db12f50fb317606054a817f66befb99ca12446f 100644
--- a/arbor/morph/region.cpp
+++ b/arbor/morph/region.cpp
@@ -173,7 +173,7 @@ mcable_list remove_covered_points(mcable_list cables, const morphology& m) {
 
 // Empty region.
 
-struct nil_ {};
+struct nil_: region_tag {};
 
 region nil() {
     return region{nil_{}};
@@ -190,7 +190,8 @@ std::ostream& operator<<(std::ostream& o, const nil_& x) {
 
 // Explicit cable section.
 
-struct cable_ {
+struct cable_: region_tag {
+    explicit cable_(mcable c): cable(std::move(c)) {}
     mcable cable;
 };
 
@@ -220,7 +221,8 @@ std::ostream& operator<<(std::ostream& o, const cable_& c) {
 
 // Region with all segments with the same numeric tag.
 
-struct tagged_ {
+struct tagged_: region_tag {
+    explicit tagged_(int tag): tag(tag) {}
     int tag;
 };
 
@@ -284,7 +286,7 @@ std::ostream& operator<<(std::ostream& o, const tagged_& t) {
 
 // Region comprising whole morphology.
 
-struct all_ {};
+struct all_: region_tag {};
 
 region all() {
     return region(all_{});
@@ -307,7 +309,8 @@ std::ostream& operator<<(std::ostream& o, const all_& t) {
 
 // Named region.
 
-struct named_ {
+struct named_: region_tag {
+    explicit named_(std::string name): name(std::move(name)) {}
     std::string name;
 };
 
@@ -326,7 +329,7 @@ std::ostream& operator<<(std::ostream& o, const named_& x) {
 
 // Intersection of two regions.
 
-struct reg_and {
+struct reg_and: region_tag {
     region lhs;
     region rhs;
     reg_and(region lhs, region rhs): lhs(std::move(lhs)), rhs(std::move(rhs)) {}
@@ -373,7 +376,7 @@ std::ostream& operator<<(std::ostream& o, const reg_and& x) {
 
 // Union of two regions.
 
-struct reg_or {
+struct reg_or: region_tag {
     region lhs;
     region rhs;
     reg_or(region lhs, region rhs): lhs(std::move(lhs)), rhs(std::move(rhs)) {}
@@ -412,6 +415,8 @@ region::region() {
     *this = reg::nil();
 }
 
+// Implicit constructors/converters.
+
 region::region(std::string label) {
     *this = reg::named(std::move(label));
 }
@@ -420,4 +425,13 @@ region::region(const char* label) {
     *this = reg::named(label);
 }
 
+region::region(mcable c) {
+    *this = reg::cable(c.branch, c.prox_pos, c.dist_pos);
+}
+
+region::region(const mcable_list& cl) {
+    *this = std::accumulate(cl.begin(), cl.end(), reg::nil(),
+        [](auto& rg, auto& p) { return join(rg, region(p)); });
+}
+
 } // namespace arb
diff --git a/test/unit/test_cv_policy.cpp b/test/unit/test_cv_policy.cpp
index aa11e16782001a8dc4bfcbc496f26a1ab366a2f1..1f42c41f72fa7b17b654f4950405fd61789ad45e 100644
--- a/test/unit/test_cv_policy.cpp
+++ b/test/unit/test_cv_policy.cpp
@@ -51,15 +51,7 @@ namespace {
 
     template <typename... A>
     locset as_locset(mlocation head, A... tail) {
-        return join(ls::location(head), ls::location(tail)...);
-    }
-
-    template <typename Seq>
-    locset as_locset(const Seq& seq) {
-        using std::begin;
-        using std::end;
-        return std::accumulate(begin(seq), end(seq), ls::nil(),
-            [](locset ls, const mlocation& p) { return join(std::move(ls), ls::location(p)); });
+        return join(locset(head), locset(tail)...);
     }
 }
 
diff --git a/test/unit/test_morph_expr.cpp b/test/unit/test_morph_expr.cpp
index 96cfae6e646f6d31a4c56c5c1e59844671c27576..60d104e992458d8b64e0ad760c0f0e17b4d3317f 100644
--- a/test/unit/test_morph_expr.cpp
+++ b/test/unit/test_morph_expr.cpp
@@ -92,7 +92,7 @@ TEST(locset, expr_repn) {
     auto root = ls::root();
     auto term = ls::terminal();
     auto samp = ls::sample(42);
-    auto loc = ls::location({2, 0.5});
+    auto loc = ls::location(2, 0.5);
 
     EXPECT_EQ(to_string(root), "(root)");
     EXPECT_EQ(to_string(term), "(terminal)");
@@ -103,14 +103,14 @@ TEST(locset, expr_repn) {
     EXPECT_EQ(to_string(loc), "(location 2 0.5)");
 }
 
-TEST(region, invalid_mlocation) {
+TEST(locset, invalid_mlocation) {
     // Location positions have to be in the range [0,1].
-    EXPECT_NO_THROW(ls::location({123, 0.0}));
-    EXPECT_NO_THROW(ls::location({123, 0.02}));
-    EXPECT_NO_THROW(ls::location({123, 1.0}));
+    EXPECT_NO_THROW(ls::location(123, 0.0));
+    EXPECT_NO_THROW(ls::location(123, 0.02));
+    EXPECT_NO_THROW(ls::location(123, 1.0));
 
-    EXPECT_THROW(ls::location({0, 1.5}), invalid_mlocation);
-    EXPECT_THROW(ls::location({unsigned(-1), 0.}), invalid_mlocation);
+    EXPECT_THROW(ls::location(0, 1.5), invalid_mlocation);
+    EXPECT_THROW(ls::location(unsigned(-1), 0.), invalid_mlocation);
 }
 
 // Name evaluation (thingify) tests:
@@ -204,13 +204,14 @@ TEST(locset, thingify) {
     auto root = ls::root();
     auto term = ls::terminal();
     auto samp = ls::sample(4);
-    auto midb2 = ls::location({2, 0.5});
-    auto midb1 = ls::location({1, 0.5});
-    auto begb0 = ls::location({0, 0});
-    auto begb1 = ls::location({1, 0});
-    auto begb2 = ls::location({2, 0});
-    auto begb3 = ls::location({3, 0});
-    auto begb4 = ls::location({4, 0});
+    auto midb2 = ls::location(2, 0.5);
+    auto midb1 = ls::location(1, 0.5);
+    auto begb0 = ls::location(0, 0);
+    auto begb1 = ls::location(1, 0);
+    auto begb2 = ls::location(2, 0);
+    auto begb3 = ls::location(3, 0);
+    auto begb4 = ls::location(4, 0);
+    auto multi = sum(begb3, midb2, midb1, midb2);
 
     // Eight samples
     //
@@ -245,6 +246,11 @@ TEST(locset, thingify) {
         EXPECT_EQ(thingify(begb2, mp), (ll{{2,0}}));
         EXPECT_EQ(thingify(begb3, mp), (ll{{3,0}}));
         EXPECT_EQ(thingify(begb4, mp), (ll{{4,0}}));
+
+        // Check round-trip of implicit locset conversions.
+        // (Use a locset which is non-trivially a multiset in order to
+        // test the fold in the constructor.)
+        EXPECT_EQ(thingify(multi, mp), thingify(locset(thingify(multi, mp)), mp));
     }
     {
         mprovider mp(morphology(sm, false));
@@ -315,6 +321,10 @@ TEST(region, thingify) {
         EXPECT_TRUE(cablelist_eq(thingify(join(h1, t1), mp), (cl{{0, 0, 0.7}})));
         EXPECT_TRUE(cablelist_eq(thingify(join(h1, t2), mp), (cl{{0, 0, 0.5}, {0, 0.7, 1}})));
         EXPECT_TRUE(cablelist_eq(thingify(intersect(h2, t1), mp), (cl{{0, 0.5, 0.7}})));
+
+        // Check round-trip of implicit region conversions.
+        EXPECT_EQ((mcable_list{{0, 0.3, 0.6}}), thingify(region(mcable{0, 0.3, 0.6}), mp));
+        EXPECT_TRUE(cablelist_eq(t2_, thingify(region(t2_), mp)));
     }