diff --git a/CHANGELOG.md b/CHANGELOG.md
index 49eb223575b27dbab80c7216b3bffbf4ddc6d1f6..039133630f7c9e7a9467a1be2ec8cb6923ef4481 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,9 @@ SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception
 
 ## C++: Changelog
 
+- `Dune::IteratorRange` now supports different types for begin and end iterator
+  to model C++20's sentinel terminated ranges.
+
 - Add preprocessor macro `DUNE_FORCE_INLINE` as a portable attribute to force inlining of functions (if supported).
 
 - Add `bit_width` and `countl_zero` overloads for `bigunsignedint` objects.
diff --git a/dune/common/iteratorrange.hh b/dune/common/iteratorrange.hh
index b897128156e9a9e8e46a8d43911d7c501f03321b..268ba411bc85c38a95b5fa2e155c981da256f647 100644
--- a/dune/common/iteratorrange.hh
+++ b/dune/common/iteratorrange.hh
@@ -14,10 +14,14 @@ namespace Dune {
    * existing containers that lack a standard begin(), end()
    * pair of member functions.
    *
+   * This supports to use a different type for the end iterator
+   * (a so called sentinel).
+   *
    * \tparam Iterator  The type of iterator
+   * \tparam Sentinel  The type of the end iterator (default=Iterator)
    * \ingroup CxxUtilities
    */
-  template<typename Iterator>
+  template<typename Iterator, typename Sentinel=Iterator>
   class IteratorRange
   {
 
@@ -26,6 +30,9 @@ namespace Dune {
     //! The iterator belonging to this range.
     typedef Iterator iterator;
 
+    //! The iterator belonging to this range.
+    typedef Sentinel sentinel;
+
     //! The iterator belonging to this range.
     /**
      * This typedef is here mainly for compatibility reasons.
@@ -33,9 +40,9 @@ namespace Dune {
     typedef Iterator const_iterator;
 
     //! Constructs an iterator range on [begin,end).
-    IteratorRange(const Iterator& begin, const Iterator& end)
-      : _begin(begin)
-      , _end(end)
+    IteratorRange(const Iterator& begin, const Sentinel& end)
+      : begin_(begin)
+      , end_(end)
     {}
 
     //! Default constructor, relies on iterators being default-constructible.
@@ -45,19 +52,19 @@ namespace Dune {
     //! Returns an iterator pointing to the begin of the range.
     iterator begin() const
     {
-      return _begin;
+      return begin_;
     }
 
     //! Returns an iterator pointing past the end of the range.
-    iterator end() const
+    sentinel end() const
     {
-      return _end;
+      return end_;
     }
 
   private:
 
-    Iterator _begin;
-    Iterator _end;
+    Iterator begin_;
+    Sentinel end_;
 
   };
 
diff --git a/dune/common/test/rangeutilitiestest.cc b/dune/common/test/rangeutilitiestest.cc
index 729a6ed8ad6453a7c3acd4b11f6ef7d5bc708602..a4847f0bbf753afcc88d89783999a1967d49ecbb 100644
--- a/dune/common/test/rangeutilitiestest.cc
+++ b/dune/common/test/rangeutilitiestest.cc
@@ -92,7 +92,7 @@ auto checkSameRange(R1&& r1, BeginIt2&& it2, EndIt2&& end2)
 {
   auto it1 = r1.begin();
   auto end1 = r1.end();
-  for(; (it1 < end1) and (it2 < end2); ++it1, ++it2)
+  for(; (it1 != end1) and (it2 != end2); ++it1, ++it2)
     if (*it1 != *it2)
       return false;
   if ((it1 != end1) or (it2 != end2))
@@ -387,11 +387,64 @@ auto testSparseRange()
 
 
 
+// An empty class tagging the end of a range
+class SentinelIterator
+{};
+
+// Classical use case for sentinels: An iterator for
+// a null-terminated string. Using a sentinel allows
+// to implement a range without a linear scan to find
+// the end or storing an additional isEnd flag in the
+// iterator.
+class NullTerminatedStringIterator
+{
+public:
+  NullTerminatedStringIterator(char* p)
+    : p_(p)
+  {}
+
+  NullTerminatedStringIterator& operator++()
+  {
+    ++p_;
+    return *this;
+  }
+
+  char& operator*()
+  {
+    return *p_;
+  }
+
+  friend bool operator==(const NullTerminatedStringIterator& it, const SentinelIterator& end)
+  {
+    return (*it.p_) == 0;
+  }
+
+private:
+  char* p_;
+};
+
+
+
+Dune::TestSuite testIteratorRange()
+{
+  Dune::TestSuite suite("Check IteratorRange with sentinel");
+
+  // Check sentinel terminated range for null-terminated string
+  char testStr[] = "FooBar";
+  auto range_sentinel = Dune::IteratorRange(NullTerminatedStringIterator(testStr), SentinelIterator());
+  suite.check(checkSameRange(range_sentinel, std::string_view(testStr)));
+
+  return suite;
+}
+
+
+
 int main()
 {
   // Check IsIterable<> for https://gitlab.dune-project.org/core/dune-common/issues/58
   static_assert(Dune::IsIterable< std::array<int, 3> >::value, "std::array<int> must be a range");
   static_assert(Dune::IsIterable< Dune::IteratorRange<int*> >::value, "IteratorRange must be a range");
+//  static_assert(Dune::IsIterable< Dune::IteratorRange<int*, void*> >::value, "IteratorRange must be a range");
   static_assert(!Dune::IsIterable< int >::value, "int must not be a range");
 
   Dune::TestSuite suite;
@@ -505,6 +558,8 @@ int main()
 
   suite.subTest(testSparseRange());
 
+  suite.subTest(testIteratorRange());
+
   return suite.exit();
 
 }