diff --git a/cmake/modules/CheckCXXFeatures.cmake b/cmake/modules/CheckCXXFeatures.cmake
index 19c4e60c8edea9905b660c269f4ad414f09e6063..c4075d4114ba40bf2afef3a5edf49f273c359c28 100644
--- a/cmake/modules/CheckCXXFeatures.cmake
+++ b/cmake/modules/CheckCXXFeatures.cmake
@@ -33,6 +33,7 @@ include(CMakePushCheckState)
 include(CheckCXXCompilerFlag)
 include(CheckIncludeFileCXX)
 include(CheckCXXSourceCompiles)
+include(CheckCXXSymbolExists)
 
 # C++ standard versions that this test knows about
 set(CXX_VERSIONS 17 14 11)
@@ -464,8 +465,69 @@ check_cxx_source_compiles("
 " DUNE_SUPPORTS_CXX_THROW_IN_CONSTEXPR
   )
 
+
+# ******************************************************************************
+#
+# Checks for standard library features
+#
+# While there are __cpp_lib_* feature test macros for all of these, those are
+# unfortunately unreliable, as libc++ does not have feature test macros yet.
+#
+# In order to keep the tests short, they use check_cxx_symbol_exists(). That
+# function can only test for macros and linkable symbols, however, so we wrap
+# tested types into a call to std::move(). That should be safe, as std::move()
+# does not require a complete type.
+#
+# ******************************************************************************
+
 # Check whether we have <experimental/type_traits> (for is_detected et. al.)
 check_include_file_cxx(
   experimental/type_traits
   DUNE_HAVE_HEADER_EXPERIMENTAL_TYPE_TRAITS
   )
+
+check_cxx_symbol_exists(
+  "std::make_unique<int>"
+  memory
+  DUNE_HAVE_CXX_MAKE_UNIQUE
+  )
+
+check_cxx_symbol_exists(
+  "std::move<std::bool_constant<true>>"
+  "utility;type_traits"
+  DUNE_HAVE_CXX_BOOL_CONSTANT
+  )
+
+if (NOT DUNE_HAVE_CXX_BOOL_CONSTANT)
+  check_cxx_symbol_exists(
+    "std::move<std::experimental::bool_constant<true>>"
+    "utility;experimental/type_traits"
+    DUNE_HAVE_CXX_EXPERIMENTAL_BOOL_CONSTANT
+    )
+endif()
+
+check_cxx_symbol_exists(
+  "std::apply<std::negate<int>,std::tuple<int>>"
+  "functional;tuple"
+  DUNE_HAVE_CXX_APPLY
+  )
+
+if (NOT DUNE_HAVE_CXX_APPLY)
+  check_cxx_symbol_exists(
+    "std::experimental::apply<std::negate<int>,std::tuple<int>>"
+    "functional;experimental/tuple"
+    DUNE_HAVE_CXX_EXPERIMENTAL_APPLY
+  )
+endif()
+
+check_cxx_symbol_exists(
+  "std::experimental::make_array<int,int>"
+  "experimental/array"
+  DUNE_HAVE_CXX_EXPERIMENTAL_MAKE_ARRAY
+  )
+
+check_cxx_symbol_exists(
+  "std::move<std::experimental::detected_t<std::decay_t,int>>"
+  "utility;experimental/type_traits"
+  DUNE_HAVE_CXX_EXPERIMENTAL_IS_DETECTED
+  )
diff --git a/config.h.cmake b/config.h.cmake
index e0caa8f5894a899687bdc1e87ad88e7a121dc169..1af81555145f43c852c42e2a7d6fd7a4aa914bbd 100644
--- a/config.h.cmake
+++ b/config.h.cmake
@@ -35,6 +35,27 @@
 /* does the standard library provide <experimental/type_traits> ? */
 #cmakedefine DUNE_HAVE_HEADER_EXPERIMENTAL_TYPE_TRAITS 1
 
+/* does the standard library provide make_unique() ? */
+#cmakedefine DUNE_HAVE_CXX_MAKE_UNIQUE 1
+
+/* does the standard library provide bool_constant ? */
+#cmakedefine DUNE_HAVE_CXX_BOOL_CONSTANT 1
+
+/* does the standard library provide experimental::bool_constant ? */
+#cmakedefine DUNE_HAVE_CXX_EXPERIMENTAL_BOOL_CONSTANT 1
+
+/* does the standard library provide apply() ? */
+#cmakedefine DUNE_HAVE_CXX_APPLY 1
+
+/* does the standard library provide experimental::apply() ? */
+#cmakedefine DUNE_HAVE_CXX_EXPERIMENTAL_APPLY 1
+
+/* does the standard library provide experimental::make_array() ? */
+#cmakedefine DUNE_HAVE_CXX_EXPERIMENTAL_MAKE_ARRAY 1
+
+/* does the standard library provide experimental::is_detected ? */
+#cmakedefine DUNE_HAVE_CXX_EXPERIMENTAL_IS_DETECTED 1
+
 /* Define if you have a BLAS library. */
 #cmakedefine HAVE_BLAS 1