Как представить шаблонный тип с переменным числом аргументов на основе другой группы аргументов шаблона с переменным числом аргументов в современном С++?

Предположим, у меня есть следующая структура вариативного шаблона:

template <class... T> 
struct Example {};

Теперь я хочу определить функцию шаблона:

template<class... S>
??? f() {
    return Example<???>
}

где специализация Example<> зависит от параметра шаблона S из f.

Чтобы быть более конкретным (и простым), теперь я просто хочу вернуть Example<int, ...,int>, где число int — это размер пакета параметров S.

Как это можно сделать на современном С++, то есть на С++ 11/14/17?

В более общем смысле, есть ли способ во время компиляции определить функцию для параметров шаблона?


person HanXu    schedule 08.04.2017    source источник
comment
Итак, для какой из языковых версий? Обычно следует указывать только один.   -  person tambre    schedule 08.04.2017
comment
@tambre, С++ 17 в порядке.   -  person HanXu    schedule 08.04.2017


Ответы (2)


При выполнении метапрограммирования шаблонов, 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
comment
Отличный подход! Спасибо! - person HanXu; 09.04.2017

Вы можете создать тип сопоставления:

// Maps some type T to the type U.
template <typename T, typename U>
using Map = U;

который вы можете использовать следующим образом:

template<class... S>
Example<Map<S, int>...> f() {
    return Example<Map<S, int>...>{};
}
person Cornstalks    schedule 08.04.2017
comment
Обратите внимание, что такой using подойдет только в том случае, если вы хотите сопоставить все типы с одним значением по умолчанию. (Например, все до int). Причина: Вы не можете специализировать его. - person Daniel Jour; 08.04.2017
comment
Умный и крутой! Большое спасибо! Но на самом деле, как сказал Дэниел, такой подход можно использовать только для идентичного отображения:) - person HanXu; 09.04.2017