diff --git a/src/util/range.hpp b/src/util/range.hpp
index 2ed703df96ae92dd1d8130a64ff00fe7dddeb501..6c161fda604aa396227a5f0894b006c8e11f3692 100644
--- a/src/util/range.hpp
+++ b/src/util/range.hpp
@@ -151,6 +151,9 @@ range<U, V> make_range(const U& left, const V& right) {
     return range<U, V>(left, right);
 }
 
+// Present a possibly sentinel-terminated range as an STL-compatible sequence
+// using the sentinel_iterator adaptor.
+
 template <typename Seq>
 auto canonical_view(Seq& s) ->
     range<sentinel_iterator_t<decltype(std::begin(s)), decltype(std::end(s))>>
@@ -165,6 +168,21 @@ auto canonical_view(const Seq& s) ->
     return {make_sentinel_iterator(std::begin(s), std::end(s)), make_sentinel_end(std::begin(s), std::end(s))};
 }
 
+// Strictly evaluate end point in sentinel-terminated range and present as a range over
+// iterators. Note: O(N) behaviour with forward iterator ranges or sentinel-terminated ranges.
+
+template <typename Seq>
+auto strict_view(Seq& s) -> range<decltype(std::begin(s))>
+{
+    return make_range(std::begin(s), std::next(util::upto(std::begin(s), std::end(s))));
+}
+
+template <typename Seq>
+auto strict_view(const Seq& s) -> range<decltype(std::begin(s))>
+{
+    return make_range(std::begin(s), std::next(util::upto(std::begin(s), std::end(s))));
+}
+
 } // namespace util
 } // namespace mc
 } // namespace nest
diff --git a/src/util/rangeutil.hpp b/src/util/rangeutil.hpp
index 95200b9c9a5b37c6a02bc236aa1d86bec8dac629..d87edc93be85b7211717d979bbbd86506b174574 100644
--- a/src/util/rangeutil.hpp
+++ b/src/util/rangeutil.hpp
@@ -75,9 +75,18 @@ AssignableContainer& assign_by(AssignableContainer& c, const Seq& seq, const Pro
 }
 
 // Sort in-place
+// Note that a const range reference may wrap non-const iterators.
 
 template <typename Seq>
-void sort(Seq& seq) {
+enable_if_t<!std::is_const<typename util::sequence_traits<Seq>::reference>::value>
+sort(Seq& seq) {
+    auto canon = canonical_view(seq);
+    std::sort(std::begin(canon), std::end(canon));
+}
+
+template <typename Seq>
+enable_if_t<!std::is_const<typename util::sequence_traits<Seq>::reference>::value>
+sort(const Seq& seq) {
     auto canon = canonical_view(seq);
     std::sort(std::begin(canon), std::end(canon));
 }
@@ -85,7 +94,20 @@ void sort(Seq& seq) {
 // Sort in-place by projection `proj`
 
 template <typename Seq, typename Proj>
-void sort_by(Seq& seq, const Proj& proj) {
+enable_if_t<!std::is_const<typename util::sequence_traits<Seq>::reference>::value>
+sort_by(Seq& seq, const Proj& proj) {
+    using value_type = typename sequence_traits<Seq>::value_type;
+    auto canon = canonical_view(seq);
+
+    std::sort(std::begin(canon), std::end(canon),
+        [&proj](const value_type& a, const value_type& b) {
+            return proj(a) < proj(b);
+        });
+}
+
+template <typename Seq, typename Proj>
+enable_if_t<!std::is_const<typename util::sequence_traits<Seq>::reference>::value>
+sort_by(const Seq& seq, const Proj& proj) {
     using value_type = typename sequence_traits<Seq>::value_type;
     auto canon = canonical_view(seq);
 
diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt
index 1c286863c3e92a9c8f2a11eba8679f66094b3c72..b67683ecebd803018185f9b09b18be7a774dd097 100644
--- a/tests/unit/CMakeLists.txt
+++ b/tests/unit/CMakeLists.txt
@@ -1,13 +1,3 @@
-set(HEADERS
-    ${PROJECT_SOURCE_DIR}/src/cell.hpp
-    ${PROJECT_SOURCE_DIR}/src/cell_tree.hpp
-    ${PROJECT_SOURCE_DIR}/src/math.hpp
-    ${PROJECT_SOURCE_DIR}/src/point.hpp
-    ${PROJECT_SOURCE_DIR}/src/segment.hpp
-    ${PROJECT_SOURCE_DIR}/src/swcio.hpp
-    ${PROJECT_SOURCE_DIR}/src/tree.hpp
-)
-
 set(TEST_SOURCES
     # unit tests
     test_algorithms.cpp
@@ -47,7 +37,7 @@ set(TEST_SOURCES
 )
 
 add_definitions("-DDATADIR=\"${CMAKE_SOURCE_DIR}/data\"")
-add_executable(test.exe ${TEST_SOURCES} ${HEADERS})
+add_executable(test.exe ${TEST_SOURCES})
 
 set(TARGETS test.exe)
 
diff --git a/tests/unit/test_range.cpp b/tests/unit/test_range.cpp
index 3cf2893e451c9674e85ba7ffa384a24e3a6eb6f5..1f1e3cb60847ba896eb3bd2ffed447f43dc0c93b 100644
--- a/tests/unit/test_range.cpp
+++ b/tests/unit/test_range.cpp
@@ -184,6 +184,16 @@ TEST(range, sentinel) {
     EXPECT_EQ(0u, empty_cstr_range.size());
 }
 
+TEST(range, strictify) {
+    const char *cstr = "hello world";
+    auto cstr_range = util::make_range(cstr, null_terminated);
+
+    auto ptr_range = util::strict_view(cstr_range);
+    EXPECT_TRUE((std::is_same<decltype(ptr_range), util::range<const char *>>::value));
+    EXPECT_EQ(cstr, ptr_range.left);
+    EXPECT_EQ(cstr+11, ptr_range.right);
+}
+
 template <typename V>
 class counter_range: public ::testing::Test {};
 
@@ -290,12 +300,15 @@ TEST(range, sort) {
 
     auto cstr_range = util::make_range(std::begin(cstr), null_terminated);
 
+    // Alas, no forward_iterator sort yet, so make a strict (non-sentinel)
+    // range to sort on below
+
     // simple sort
-    util::sort(cstr_range);
+    util::sort(util::strict_view(cstr_range));
     EXPECT_EQ(std::string("dhowy"), cstr);
 
     // reverse sort by transform c to -c
-    util::sort_by(cstr_range, [](char c) { return -c; });
+    util::sort_by(util::strict_view(cstr_range), [](char c) { return -c; });
     EXPECT_EQ(std::string("ywohd"), cstr);
 }