Сначала нам нужно несколько вспомогательных шаблонов.Первый проверяет, совпадают ли два типа:
template <typename T, typename U>
struct is_same
{
// Default case: T and U are not the same type
static const bool value = false;
};
template <typename T>
struct is_same<T, T>
{
// Specialization: both template arguments are of the same type
static const bool value = true;
};
Теперь мы можем использовать логическое значение is_same<T, U>::value
, чтобы определить, эквивалентны ли типы T
и U
.
Используя is_same
, мы можем теперь написать шаблон, который проверяет, встречается ли определенный тип в списке типов:
template <typename TList, typename T>
struct contains
{
static const bool value =
is_same<typename TList::head, T>::value // Base case
|| contains<typename TList::tail, T>::value; // Recursion
};
template <typename T>
struct contains<nulltype, T>
{
// Termination condition
static const bool value = false;
};
Это рекурсивный шаблон, использующий специализацию на nulltype
для завершения рекурсии.
Один последний вспомогательный шаблон, который нам нужен, это enable_if
:
template <bool Cond, typename T=void>
struct enable_if
{
// Default case: Cond assumed to be false, no typedef
};
template <typename T>
struct enable_if<true, T>
{
// Specialization: Cond is true, so typedef
typedef T type;
};
enable_if<true, T>::type
и T
, тогда как enable_if<false, T>::type
не определено.Мы используем правило SFINAE , чтобы включить или отключить функцию в зависимости от аргумента шаблона, например:
template <typename TList>
class OutputClass
{
public:
// Only enable the function if T is in TList (by SFINAE)
template <typename T>
typename enable_if<contains<TList, T>::value>::type
output(T t)
{
std::cout << t << std::endl;
}
};
Довольно поездка, но если вы понимаете все это, вы хорошона вашем пути освоить шаблон метапрограммирования.Для более глубокого обсуждения шаблонного метапрограммирования я рекомендую вам взять копию C ++ Template Metaprogramming (ISBN-13: 9780321227256).