При выполнении метапрограммирования шаблонов, IMO, всегда помогает как можно дольше отделять «мета-часть» от «не-мета-части». Таким образом, вы можете сначала думать о «мета-части», как если бы это была обычная программа, работающая с типами, а не со значениями. Таким образом, шаблон становится функцией, принимающей некоторые типы (или для «программирования более высокого порядка»: другие шаблоны) в качестве входных данных и возвращающей некоторый тип (или для «программирования более высокого порядка»: другие шаблоны).
Итак, во-первых, отступите на секунду и не думайте о шаблонах, метапрограммировании и тому подобном. У вас есть список S
. Для каждого элемента S
вы хотите вызвать некоторую функцию и составить список возвращаемых элементов. Итак, вам нужна функция, которая по одному элементу списка возвращает элемент, с которым он сопоставлен. Назовем эту функцию mapping
. Вам также нужна функция, которая берет указанную функцию и применяет ее к вашему списку, то есть вызывает mapping
для каждого элемента и собирает список результатов. Назовем это map
.
Теперь превратите это в метапрограмму:
// mapping :: TYPE -> TYPE
// ---------------------------------------------------------
// ?? --> int (default "value")
template<typename X> struct mapping {
using type = int;
};
// if instead you want it to be undefined for unknown types:
//template<typename X> struct mapping;
// bool --> double
template<> struct mapping<bool> {
using type = double;
};
Теперь map
, обобщенный таким образом, что он может использовать что-то вроде mapping
:
// map :: ([T] -> T) -> (T -> T) -> ([T] -> T)
// "List" "Mapping" result "type" (also a "List")
// --------------------------------------------------------
template<template<typename...> class List,
template<typename> class Mapping>
struct map {
template<typename... Elements>
using type = List<typename Mapping<Elements>::type...>;
};
Наконец, примените к вашему Example
(это своего рода список, потому что он «содержит» несколько типов) и конкретному mapping
:
template<typename... S>
using MappedExample = map<Example, mapping>::type<S...>;
Теперь у вас есть результирующий шаблон, используйте его в своей неметапрограмме:
template<typename... S>
MappedExample<S...> f() {
return MappedExample<S...>{};
}
Живой пример:
int main() {
std::cout
<< typeid(Example<bool,int,char,double>).name()
<< std::endl
<< typeid(decltype(f<bool, int, char, double>())).name()
<< std::endl;
}
Выход:
7ExampleIJbicdEE
в первой строке означает Example
с параметрами шаблона bool, int, char, d ouble.
7ExampleIJdiiiEE
во второй строке означает Example
с параметрами шаблона double (сопоставление с bool) и 3 int (сопоставление по умолчанию).
person
Daniel Jour
schedule
08.04.2017