--- /dev/null
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2023 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+#ifndef dealii_cxx20_type_traits_h
+#define dealii_cxx20_type_traits_h
+
+#include <deal.II/base/config.h>
+
+#ifdef DEAL_II_HAVE_CXX20
+# include <type_traits>
+#endif
+
+DEAL_II_NAMESPACE_OPEN
+
+namespace std_cxx20
+{
+#ifdef DEAL_II_HAVE_CXX20
+ using std::type_identity;
+ using std::type_identity_t;
+#else
+ /**
+ * A template class that simply exports its template argument as a local
+ * alias. This class, while at first appearing useless, makes sense in the
+ * following context: if you have a function template as follows:
+ * @code
+ * template <typename T>
+ * void f(T, T);
+ * @endcode
+ * then it can't be called in an expression like <code>f(1, 3.141)</code>
+ * because the type <code>T</code> of the template can not be deduced in a
+ * unique way from the types of the arguments. However, if the template is
+ * written as
+ * @code
+ * template <typename T>
+ * void f(T, typename identity<T>::type);
+ * @endcode
+ * then the call becomes valid: the type <code>T</code> is not deducible
+ * from the second argument to the function, so only the first argument
+ * participates in template type resolution.
+ *
+ * The context for this feature is as follows: consider
+ * @code
+ * template <typename RT, typename A>
+ * void forward_call(RT (*p) (A), A a)
+ * {
+ * p(a);
+ * }
+ *
+ * void h (double);
+ *
+ * void g()
+ * {
+ * forward_call(&h, 1);
+ * }
+ * @endcode
+ * This code fails to compile because the compiler can't decide whether the
+ * template type <code>A</code> should be <code>double</code> (from the
+ * signature of the function given as first argument to
+ * <code>forward_call</code>, or <code>int</code> because the expression
+ * <code>1</code> has that type. Of course, what we would like the compiler
+ * to do is simply cast the <code>1</code> to <code>double</code>. We can
+ * achieve this by writing the code as follows:
+ * @code
+ * template <typename RT, typename A>
+ * void forward_call(RT (*p) (A), typename identity<A>::type a)
+ * {
+ * p(a);
+ * }
+ *
+ * void h (double);
+ *
+ * void g()
+ * {
+ * forward_call(&h, 1);
+ * }
+ * @endcode
+ */
+ template <typename T>
+ struct type_identity
+ {
+ using type = T;
+ };
+
+ template <class T>
+ using type_identity_t = typename type_identity<T>::type;
+
+#endif
+} // namespace std_cxx20
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif