diff --git a/dune/common/CMakeLists.txt b/dune/common/CMakeLists.txt
index 5b63b16237c22e8d4f4677d398bb90839d7cf0b4..efcfbb6d061f8a84d8636730c8d2721787aba02c 100644
--- a/dune/common/CMakeLists.txt
+++ b/dune/common/CMakeLists.txt
@@ -74,6 +74,7 @@ install(FILES
         math.hh
         matvectraits.hh
         nullptr.hh
+        overloadset.hh
         parametertree.hh
         parametertreeparser.hh
         path.hh
diff --git a/dune/common/overloadset.hh b/dune/common/overloadset.hh
new file mode 100644
index 0000000000000000000000000000000000000000..4f0aaf663a78eb72cd9f0bf6d6ee191193c9cf45
--- /dev/null
+++ b/dune/common/overloadset.hh
@@ -0,0 +1,166 @@
+// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// vi: set et ts=4 sw=2 sts=2:
+#ifndef DUNE_COMMON_OVERLOADSET_HH
+#define DUNE_COMMON_OVERLOADSET_HH
+
+#include <utility>
+#include <type_traits>
+
+#include <dune/common/std/type_traits.hh>
+
+
+
+namespace Dune {
+
+namespace Impl {
+
+  // This overload set derives from
+  // all passed functions. Since we
+  // cannot do argument pack expansion
+  // on using statements this is done recursively.
+  template<class F0, class... F>
+  class OverloadSet: public OverloadSet<F...>, F0
+  {
+    using Base = OverloadSet<F...>;
+  public:
+
+    template<class FF0, class... FF>
+    OverloadSet(FF0&& f0, FF&&... ff) :
+      Base(std::forward<FF>(ff)...),
+      F0(std::forward<FF0>(f0))
+    {}
+
+    using F0::operator();
+    using Base::operator();
+  };
+
+  template<class F0>
+  class OverloadSet<F0>: public F0
+  {
+  public:
+
+    template<class FF0>
+    OverloadSet(FF0&& f0) :
+      F0(std::forward<FF0>(f0))
+    {}
+
+    using F0::operator();
+  };
+
+} // end namespace Impl
+
+
+
+/**
+ * \brief Create an overload set
+ *
+ * \tparam F List of function object types
+ * \param f List of function objects
+ *
+ * This returns an object that contains all
+ * operator() implementations of the passed
+ * functions. All those are available when
+ * calling operator() of the returned object.
+ *
+ * The returned object derives from
+ * those implementations such that it contains
+ * all operator() implementations in its
+ * overload set. When calling operator()
+ * this will select the best overload.
+ * If multiple overload are equally good this
+ * will lead to ambiguity.
+ *
+ * Notice that the passed function objects are
+ * stored by value and must be copy-constructible.
+ */
+template<class... F>
+auto overload(F&&... f)
+{
+  return Impl::OverloadSet<std::decay_t<F>...>(std::forward<F>(f)...);
+}
+
+
+
+namespace Impl {
+
+  template<class F0, class... F>
+  class OrderedOverloadSet: public OrderedOverloadSet<F...>, F0
+  {
+    using Base = OrderedOverloadSet<F...>;
+  public:
+
+    template<class FF0, class... FF>
+    OrderedOverloadSet(FF0&& f0, FF&&... ff) :
+      Base(std::forward<FF>(ff)...),
+      F0(std::forward<FF0>(f0))
+    {}
+
+    template<class...  Args,
+        std::enable_if_t<Std::is_callable<F0(Args&&...)>::value, int> = 0>
+    decltype(auto) operator()(Args&&... args)
+    {
+      return F0::operator()(std::forward<Args>(args)...);
+    }
+
+    template<class...  Args,
+        std::enable_if_t< not Std::is_callable<F0(Args&&...)>::value, int> = 0>
+    decltype(auto) operator()(Args&&... args)
+    {
+      return Base::operator()(std::forward<Args>(args)...);
+    }
+
+  };
+
+  template<class F0>
+  class OrderedOverloadSet<F0>: public F0
+  {
+  public:
+
+    template<class FF0>
+    OrderedOverloadSet(FF0&& f0) :
+      F0(std::forward<FF0>(f0))
+    {}
+
+    template<class...  Args>
+    decltype(auto) operator()(Args&&... args)
+    {
+      static_assert(Std::is_callable<F0(Args&&...)>::value, "No matching overload found in OrderedOverloadSet");
+      return F0::operator()(std::forward<Args>(args)...);
+    }
+  };
+
+} // end namespace Impl
+
+
+
+/**
+ * \brief Create an ordered overload set
+ *
+ * \tparam F List of function object types
+ * \param f List of function objects
+ *
+ * This returns an object that contains all
+ * operator() implementations of the passed
+ * functions. All those are available when
+ * calling operator() of the returned object.
+ *
+ * In contrast to overload() these overloads
+ * are ordered in the sense that the first
+ * matching overload for the given arguments
+ * is selected and later ones are ignored.
+ * Hence such a call is never ambiguous.
+ *
+ * Notice that the passed function objects are
+ * stored by value and must be copy-constructible.
+ */
+template<class... F>
+auto orderedOverload(F&&... f)
+{
+  return Impl::OrderedOverloadSet<std::decay_t<F>...>(std::forward<F>(f)...);
+}
+
+
+
+} // end namespace Dune
+
+#endif // DUNE_COMMON_OVERLOADSET_HH
diff --git a/dune/common/test/CMakeLists.txt b/dune/common/test/CMakeLists.txt
index 76907985923cc8817d32c77674333acf05caa022..26115f4d003324ae869c01d771e2119392f55e07 100644
--- a/dune/common/test/CMakeLists.txt
+++ b/dune/common/test/CMakeLists.txt
@@ -131,6 +131,9 @@ dune_add_test(NAME mpihelpertest2
               LINK_LIBRARIES dunecommon
               )
 
+dune_add_test(SOURCES overloadsettest.cc
+              LINK_LIBRARIES dunecommon)
+
 dune_add_test(SOURCES parametertreelocaletest.cc
               LINK_LIBRARIES dunecommon)
 
diff --git a/dune/common/test/overloadsettest.cc b/dune/common/test/overloadsettest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..52c2935ab0f446fed7612e143a4508d1ce4fcdbe
--- /dev/null
+++ b/dune/common/test/overloadsettest.cc
@@ -0,0 +1,97 @@
+// -*- 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 <tuple>
+
+#include <dune/common/overloadset.hh>
+#include <dune/common/hybridutilities.hh>
+
+#include <dune/common/test/testsuite.hh>
+
+
+int main()
+{
+  Dune::TestSuite test;
+
+  {
+    auto foo = Dune::overload(
+            [](double i) { return 0; },
+            [](int i) { return 1; },
+            [](long i) { return 2; });
+
+    test.check(foo(3.14) == 0)
+      << "incorrect overload selected from OverloadSet";
+    test.check(foo(int(42)) == 1)
+      << "incorrect overload selected from OverloadSet";
+    test.check(foo(long(42)) == 2)
+      << "incorrect overload selected from OverloadSet";
+  }
+
+  {
+    auto foo = Dune::orderedOverload(
+            [](double i) { return 0; },
+            [](int i) { return 1; },
+            [](long i) { return 2; });
+
+    test.check(foo(3.14) == 0)
+      << "incorrect overload selected from OverloadSet";
+    test.check(foo(int(42)) == 0)
+      << "incorrect overload selected from OverloadSet";
+    test.check(foo(long(42)) == 0)
+      << "incorrect overload selected from OverloadSet";
+  }
+
+  {
+    auto foo = Dune::overload(
+            [](const int& i) { return 0; },
+            [](int&& i) { return 1; });
+
+    int i = 0;
+    test.check(foo(long(42)) == 1)
+      << "incorrect overload selected from OverloadSet";
+    test.check(foo(int(42)) == 1)
+      << "incorrect overload selected from OverloadSet";
+    test.check(foo(i) == 0)
+      << "incorrect overload selected from OverloadSet";
+  }
+
+  {
+    auto foo = Dune::orderedOverload(
+            [](const int& i) { return 0; },
+            [](int&& i) { return 1; });
+
+    int i = 0;
+    test.check(foo(long(42)) == 0)
+      << "incorrect overload selected from OverloadSet";
+    test.check(foo(int(42)) == 0)
+      << "incorrect overload selected from OverloadSet";
+    test.check(foo(i) == 0)
+      << "incorrect overload selected from OverloadSet";
+  }
+
+  {
+    auto t = std::make_tuple(42, "foo", 3.14);
+
+    auto typeToName = Dune::overload(
+            [](int) { return "int"; },
+            [](long) { return "long"; },
+            [](std::string) { return "string"; },
+            [](float) { return "float"; },
+            [](double) { return "double"; });
+
+    std::string tupleTypes;
+    Dune::Hybrid::forEach(t, [&](auto&& ti) {
+      tupleTypes += typeToName(ti);
+    });
+
+    test.check(tupleTypes == "intstringdouble")
+      << "traversal of tuple called incorrect overloads";
+  }
+
+
+  return test.exit();
+}