diff --git a/dune/subgrid/CMakeLists.txt b/dune/subgrid/CMakeLists.txt
index 5f078aad77f62ec6cb17e1a9db05778c01c5b56a..c09b0c42fa656bcabbfd5ae6fc2d9088e1427a0a 100644
--- a/dune/subgrid/CMakeLists.txt
+++ b/dune/subgrid/CMakeLists.txt
@@ -1,3 +1,4 @@
+add_subdirectory(common)
 add_subdirectory(facetools)
 add_subdirectory(subgrid)
 add_subdirectory(test)
diff --git a/dune/subgrid/common/CMakeLists.txt b/dune/subgrid/common/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a31ec91c4cfb2205d700956f5a1633d72fe8b067
--- /dev/null
+++ b/dune/subgrid/common/CMakeLists.txt
@@ -0,0 +1,5 @@
+set(commonincludedir  ${CMAKE_INSTALL_INCLUDEDIR}/dune/subgrid/common)
+set(commoninclude_HEADERS  variant.hh)
+# include not needed for CMake
+# include $(top_srcdir)/am/global-rules
+install(FILES ${commoninclude_HEADERS} DESTINATION ${commonincludedir})
diff --git a/dune/subgrid/common/variant.hh b/dune/subgrid/common/variant.hh
new file mode 100644
index 0000000000000000000000000000000000000000..e2132ea6c66de8266669eb87f8b9a44fe6143837
--- /dev/null
+++ b/dune/subgrid/common/variant.hh
@@ -0,0 +1,387 @@
+// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// vi: set et ts=4 sw=2 sts=2:
+#ifndef DUNE_COMMON_STD_VARIANT_HH
+#define DUNE_COMMON_STD_VARIANT_HH
+#include <tuple>
+#include <memory>
+#include <dune/common/hybridutilities.hh>
+#include <dune/common/exceptions.hh>
+
+namespace Dune {
+namespace Std {
+namespace Impl {
+
+  /* helper constructs to find position of a type T in a pack Ts... */
+  template <typename T, typename... Ts>
+  struct index_in_pack;
+
+  template <typename T, typename... Ts>
+  struct index_in_pack<T, T, Ts...> : std::integral_constant<std::size_t, 0> {};
+
+  template <typename T, typename U, typename... Ts>
+  struct index_in_pack<T, U, Ts...> : std::integral_constant<std::size_t, 1 + index_in_pack<T, Ts...>::value> {};
+
+  /* end helper constructs to find position of a type T in a pack Ts... */
+
+  template<typename Tp>
+  struct Buffer_ : std::aligned_storage<sizeof(Tp)> {
+    using Storage = typename std::aligned_storage_t<sizeof(Tp)>::type;
+    Storage storage_;
+
+    void* addr() {
+      return static_cast<void*>(&storage_);
+    }
+
+    const void* addr() const {
+      return static_cast<const void*>(&storage_);
+    }
+
+    Tp* ptr() {
+      return static_cast<Tp*>(addr());
+    }
+
+    const Tp* ptr() const {
+      return static_cast<const Tp*>(addr());
+    }
+  };
+
+  template<typename Tp, bool isTrivial>
+  struct TypeStorage_ { };
+
+  template<typename Tp>
+  struct TypeStorage_<Tp, true> {
+    TypeStorage_(Tp t) :
+      tp_(t) {}
+
+    template<typename... Args>
+    TypeStorage_(Args... args) :
+      tp_(args...) {}
+
+    auto& get() {
+      return tp_;
+    }
+    const auto& get() const {
+      return tp_;
+    }
+    private:
+    Tp tp_;
+  };
+
+  template<typename Tp>
+  struct TypeStorage_<Tp, false> {
+    TypeStorage_(Tp t) {
+      ::new (&tp_) Tp(t);
+    }
+
+    template<typename... Args>
+    TypeStorage_(Args... args) {
+      ::new (&tp_) Tp(std::forward<Args>(args)...);
+    }
+
+    auto& get() {
+      return *(tp_.ptr());
+    }
+    const auto& get() const {
+      return *(tp_.ptr());
+    }
+
+    private:
+    Buffer_<Tp> tp_;
+  };
+
+  template<typename... T>
+  union variant_union_ {};
+
+  template<typename Head_, typename... Tail_>
+  union variant_union_<Head_, Tail_...> {
+    constexpr variant_union_() :
+      tail_() {}
+
+    template<typename... Args>
+    constexpr variant_union_(std::integral_constant<size_t, 0>, Args&&... args) :
+      head_(std::forward<Args...>(args)...) {}
+
+    template<size_t N, typename... Args>
+    constexpr variant_union_(std::integral_constant<size_t, N>, Args&&... args) :
+      tail_(std::integral_constant<size_t, N-1>(), std::forward<Args...>(args)...) {}
+
+    auto& getByIndex(std::integral_constant<size_t, 0>) {
+      return head_.get();
+    }
+
+    const auto& getByIndex(std::integral_constant<size_t, 0>) const {
+      return head_.get();
+    }
+
+
+    template<size_t N>
+    auto& getByIndex(std::integral_constant<size_t, N>) {
+      return tail_.getByIndex(std::integral_constant<size_t, N-1>());
+    }
+
+    template<size_t N>
+    const auto& getByIndex(std::integral_constant<size_t, N>) const {
+      return tail_.getByIndex(std::integral_constant<size_t, N-1>());
+    }
+
+    template<typename Tp>
+    void set(Tp obj) {
+      Dune::Hybrid::ifElse(std::is_same<Tp, Head_>(),
+        [&](auto&& id)    { head_=std::move(id(obj)); },
+        [&](auto&& id) { return id(tail_).set(std::move(obj)); }
+      );
+    }
+
+    constexpr size_t size() const {
+      return sizeof...(Tail_)+1;
+    }
+
+    private:
+    TypeStorage_<Head_, std::is_literal_type<Head_>::value> head_;
+    variant_union_<Tail_...> tail_;
+  };
+
+  template<typename...T>
+  struct variant_{
+
+    constexpr variant_() :
+      unions_() {}
+
+    template<typename Tp>
+    constexpr variant_(Tp obj) :
+      unions_(),
+      index_(index_in_pack<Tp, T...>::value)
+      {
+        unions_.set(std::move(obj));
+      }
+
+    template<typename Tp>
+    auto& get() {
+      constexpr size_t idx = index_in_pack<Tp, T...>::value;
+      if (index_ != idx)
+        DUNE_THROW(Dune::Exception, "Bad variant access.");
+
+      return get<idx>();
+    }
+
+    template<typename Tp>
+    const auto& get() const {
+      constexpr size_t idx = index_in_pack<Tp, T...>::value;
+      if (index_ != idx)
+        DUNE_THROW(Dune::Exception, "Bad variant access.");
+
+      return get<idx>();
+    }
+
+    template<typename Tp>
+    Tp* get_if() {
+      if (not holds_alternative<Tp>())
+        return (Tp*) nullptr;
+      else
+        return &(get<Tp>());
+    }
+
+    template<typename Tp>
+    const Tp* get_if() const {
+      if (not holds_alternative<Tp>())
+        return (Tp*) nullptr;
+      else
+        return &(get<Tp>());
+    }
+
+    template<size_t N>
+    auto* get_if() {
+      using Tp = std::decay_t<decltype(get<N>())>;
+      if (not holds_alternative<N>())
+        return (Tp*) nullptr;
+      else
+        return &(get<Tp>());
+    }
+
+    template<size_t N>
+    const auto* get_if() const {
+      using Tp = std::decay_t<decltype(get<N>())>;
+      if (not holds_alternative<N>())
+        return (Tp*) nullptr;
+      else
+        return &(get<Tp>());
+    }
+
+    template<size_t N>
+    auto& get() {
+      if (index_ != N)
+        DUNE_THROW(Dune::Exception, "Bad variant access.");
+      return unions_.template getByIndex(std::integral_constant<size_t, N>());
+    }
+    template<size_t N>
+    const auto& get() const {
+      if (index_ != N)
+        DUNE_THROW(Dune::Exception, "Bad variant access.");
+      return unions_.template getByIndex(std::integral_constant<size_t, N>());
+    }
+
+    template<typename Tp>
+    constexpr Tp& operator=(Tp obj) {
+      unions_.set(std::move(obj));
+      constexpr auto index = index_in_pack<Tp, T...>::value;
+      index_=index;
+      return unions_.getByIndex(std::integral_constant<size_t,index>());
+    }
+
+    constexpr std::size_t index() const noexcept {
+      return index_;
+    }
+
+    constexpr auto size() const {
+      return sizeof...(T);
+    }
+
+    /* \brief Apply visitor to the active variant.
+     *
+     * visit assumes that the result of
+     * func(T) has the same type for all types T
+     * in this variant.
+     */
+    template<typename F>
+    auto visit(F&& func) {
+      using namespace Dune::Hybrid;
+
+      using Result = decltype(func(unions_.getByIndex(std::integral_constant<size_t, 0>())));
+
+      return ifElse(std::is_same<Result, void>(), [&, this](auto id) {
+          constexpr auto tsize = size_;
+          Dune::Hybrid::forEach(Dune::Hybrid::integralRange(std::integral_constant<size_t, tsize>()), [&](auto i) {
+            if (i==this->index_)
+              func(id(unions_).getByIndex(std::integral_constant<size_t, i>()));
+            });
+          return;},
+        [&func,this](auto id) {
+          constexpr auto tsize = size_;
+
+          auto result = std::unique_ptr<Result>();
+
+          Dune::Hybrid::forEach(Dune::Hybrid::integralRange(std::integral_constant<size_t, tsize>()), [&, this](auto i) {
+            if (i==this->index_)
+              result = std::make_unique<Result>(func(id(this->unions_).getByIndex(std::integral_constant<size_t, i>())));
+          });
+      return *result;
+       });
+    }
+
+    template<typename F>
+    auto visit(F&& func) const {
+      using namespace Dune::Hybrid;
+
+      using Result = decltype(func(unions_.getByIndex(std::integral_constant<size_t, 0>())));
+
+      return ifElse(std::is_same<Result, void>(), [&, this](auto id) {
+          constexpr auto tsize = size_;
+          Dune::Hybrid::forEach(Dune::Hybrid::integralRange(std::integral_constant<size_t, tsize>()), [&](auto i) {
+            if (i==this->index_)
+              func(id(unions_).getByIndex(std::integral_constant<size_t, i>()));
+            });
+          return;},
+        [&func,this](auto id) {
+          constexpr auto tsize = size_;
+
+          auto result = std::unique_ptr<Result>();
+
+          Dune::Hybrid::forEach(Dune::Hybrid::integralRange(std::integral_constant<size_t, tsize>()), [&, this](auto i) {
+            if (i==this->index_)
+              result = std::make_unique<Result>(func(id(this->unions_).getByIndex(std::integral_constant<size_t, i>())));
+          });
+      return *result;
+       });
+    }
+
+    /** \brief Check if a given type is the one that is currently active in the variant. */
+    template<typename Tp>
+    constexpr bool holds_alternative() const {
+      // I have no idea how this could be really constexpr, but for STL-conformity,
+      // I'll leave the modifier there.
+      return (index_in_pack<Tp, T...>::value == index_);
+    }
+
+    /** \brief Check if a given type is the one that is currently active in the variant. */
+    template<size_t N>
+    constexpr bool holds_alternative() const {
+      // I have no idea how this could be really constexpr, but for STL-conformity,
+      // I'll leave the modifier there.
+      return (N == index_);
+    }
+
+    private:
+    variant_union_<T...> unions_;
+    std::size_t index_;
+    constexpr static auto size_ = sizeof...(T);
+  };
+
+} // end namespace Impl
+
+  /** \brief Incomplete re-implementation of C++17's std::variant. */
+  template<typename ...T>
+  using variant = Impl::variant_<T...>;
+
+  template<size_t N, typename... T>
+  auto& get(variant<T...>& var) {
+    return var.template get<N>();
+  }
+
+  template<size_t N, typename... T>
+  const auto& get(const variant<T...>& var) {
+    return var.template get<N>();
+  }
+
+  template<typename F, typename... T>
+  auto visit(F&& visitor, variant<T...>& var) {
+    return var.visit(std::forward<F>(visitor));
+  }
+
+  template<typename F, typename... T>
+  auto visit(F&& visitor, const variant<T...>& var) {
+    return var.visit(std::forward<F>(visitor));
+  }
+
+  template<typename Tp, typename ...T>
+  auto& get(variant<T...>& var) {
+    return var.template get<Tp>();
+  }
+
+  template<typename Tp, typename ...T>
+  const auto& get(const variant<T...>& var) {
+    return var.template get<Tp>();
+  }
+
+  template<typename Tp, typename ...T>
+  const auto* get_if(const variant<T...>& var) {
+    return var.template get_if<Tp>();
+  }
+
+  template<typename Tp, typename ...T>
+  auto* get_if(variant<T...>& var) {
+    return var.template get_if<Tp>();
+  }
+
+  template<size_t N, typename ...T>
+  const auto* get_if(const variant<T...>& var) {
+    return var.template get_if<N>();
+  }
+
+  template<size_t N, typename ...T>
+  auto* get_if(variant<T...>& var) {
+    return var.template get_if<N>();
+  }
+
+  template<typename Tp, typename ...T>
+  constexpr bool holds_alternative(const variant<T...>& var) {
+    return var.template holds_alternative<Tp>();
+  }
+
+  template <typename... T>
+  constexpr auto variant_size_v(const variant<T...>&) {
+    return std::integral_constant<std::size_t,sizeof...(T)>::value;
+  }
+
+} // end namespace Std
+} // end namespace Dune
+#endif
diff --git a/dune/subgrid/test/CMakeLists.txt b/dune/subgrid/test/CMakeLists.txt
index 6bc998a46579b03de3f37a4c53f7281ef75ea68c..81d3862e3ca7ab6bdf798db9117e3cb103ce9039 100644
--- a/dune/subgrid/test/CMakeLists.txt
+++ b/dune/subgrid/test/CMakeLists.txt
@@ -1,5 +1,5 @@
 
-set(TESTPROGS test-w-onedgrid test-w-yaspgrid test-w-alugrid)
+set(TESTPROGS test-w-onedgrid test-w-yaspgrid test-w-alugrid testvariant)
 
 if(ALBERTA_FOUND)
 list(APPEND TESTPROGS test-w-albertagrid2d)
diff --git a/dune/subgrid/test/testvariant.cc b/dune/subgrid/test/testvariant.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ca0393f89e1c53ab03a65ce361ddeea4fe45d7bb
--- /dev/null
+++ b/dune/subgrid/test/testvariant.cc
@@ -0,0 +1,107 @@
+// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// vi: set et ts=4 sw=2 sts=2:
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <iostream>
+#include <dune/common/parallel/mpihelper.hh> // An initializer of MPI
+#include <dune/common/exceptions.hh> // We use exceptions
+#include <dune/common/test/testsuite.hh>
+
+#include <dune/subgrid/common/variant.hh>
+
+// some non-default constructible type
+struct F {
+  int i;
+  F() = delete;
+  F(int j) :
+    i(j) {}
+};
+
+Dune::TestSuite testVariant() {
+  using namespace Dune;
+  TestSuite suite;
+
+  int i = 42;
+  double d = 3.14;
+  F f(13);
+  using V = std::vector<int>;
+
+  auto variant = Std::variant<int, double, F, V>();
+
+  suite.check(Std::variant_size_v(variant) == 4, "Test variant_size_v");
+
+
+  variant = d;
+  suite.check(Std::holds_alternative<double>(variant), "Test holds_alternative");
+
+  variant = f;
+  suite.check(Std::holds_alternative<F>(variant), "Test holds_alternative");
+
+  variant = i;
+  suite.check(Std::holds_alternative<int>(variant), "Test holds_alternative");
+
+  suite.check(Std::get<int>(variant) == i, "Test get<Type>");
+  suite.check(Std::get<0>(variant) == i, "Test get<Index>");
+
+  suite.check(Std::get_if<int>(variant) != nullptr, "Test get_if on right type");
+  suite.check(Std::get_if<double>(variant) == nullptr, "Test get_if on wrong type");
+
+  suite.check(Std::get_if<0>(variant) != nullptr, "Test get_if on right index");
+  suite.check(Std::get_if<1>(variant) == nullptr, "Test get_if on wrong index");
+
+  // test if get<Type> throws if one tries to get the wrong type:
+  try {
+    // currently hold type is still int, so double should throw
+    Std::get<double>(variant);
+    suite.check(false, "Test get<Type> on wrong type should have thrown");
+  }
+  catch (...) {
+    suite.check(true, "Test get<Type> on wrong type has thrown");
+  }
+
+
+  variant = V(1);
+  suite.check(Std::get<V>(variant).size() == 1, "Test with non-trivial type");
+
+  variant = f;
+
+  suite.check(variant.index() == 2, "Test index()"); // we're at type F, which has position 2
+
+  // Demonstrate visit concept and using vector as an example of a non-POD type
+  using V2 = std::vector<double>;
+  Std::variant<V, V2> variant2;
+  variant2 = V(1);
+  auto size = [](auto&& v) {return v.size();};
+  suite.check(Std::visit(size, variant2)== 1, "Test visit");
+  variant2 = V2(2);
+  suite.check(Std::visit(size, variant2)== 2, "Test visit");
+
+  // try on a const reference:
+  const auto& constv2 = variant2;
+  suite.check(Std::visit(size, constv2)== 2, "Test const visit");
+  suite.check(Std::get_if<V2>(constv2) != nullptr, "Test const get_if");
+
+
+  return suite;
+}
+
+int main(int argc, char** argv)
+{
+  try{
+    // Maybe initialize MPI
+    Dune::MPIHelper::instance(argc, argv);
+
+    Dune::TestSuite suite;
+    suite.subTest(testVariant());
+
+    return suite.exit();
+  }
+  catch (Dune::Exception &e){
+    std::cerr << "Dune reported error: " << e << std::endl;
+  }
+  catch (...){
+    std::cerr << "Unknown exception thrown!" << std::endl;
+  }
+}