#c #templates #sfinae #variadic
#c #шаблоны #sfinae #variadic
Вопрос:
Возможно ли статически объявить N однотипных аргументов шаблона из одного аргумента шаблона целочисленного типа? Потенциально что-то похожее на это:
template < int N >
class MyTemplatedType {
// base type, known
typedef float base_t;
// instance of some other templated class with *N* template arguments
SomeOtherClass< base_t, base_t, base_t, base_t, ..., base_t > foo;
/* ... */
}
Я понимаю, что могу использовать шаблон variadic и использовать его непосредственно для объявления экземпляра члена, но мне было интересно, будет ли какая-то реализация SFINAE, которая разрешала бы один целочисленный параметр шаблона, поскольку синтаксис был бы намного чище и интуитивно понятен.
Ответ №1:
Вы можете написать мета-функцию, которая принимает N
, base_t
, и SomeOtherClass
, и рекурсивно вызывает себя с меньшим N
значением, каждый раз переходя base_t
к концу растущего пакета параметров:
template <int N, typename T, template<typename...> typename C, typename ...Ts>
struct expand {
using type = typename expand<N-1, T, C, T, Ts...>::type;
};
Для базового случая, когда N
значение уменьшается до 0, мета-функция выдает SomeOtherClass
экземпляр с набором параметров N
base_t
типов:
template <typename T, template<typename...> typename C, typename ...Ts>
struct expand<0, T, C, Ts...> {
using type = C<Ts...>;
};
Кроме того, удобно использовать псевдоним, чтобы избежать необходимости указывать typename
на сайте вызова:
template <int N, typename T, template<typename...> typename C>
using expand_t = typename expand<N, T, C>::type;
Теперь на сайте вызова вы можете написать:
expand_t<N, base_t, SomeOtherClass> foo;
// equivalent to
// SomeOtherClass< base_t, base_t, ..., base_t > foo;
// ^^^ N times ^^^
Вот демонстрация.
Комментарии:
1. Это именно то, что я искал. Некоторые замечания, хотя 1) Я понимаю, что пакет параметров
...Ts
кодирует рекурсивно растущий список аргументов шаблона, но зачем именно нам нужно передавать второйT
аргумент при рекурсивном вызове, 2) что изменится, если попытаться использовать пакет параметров в качестве аргумента функции вместо аргумента шаблона, напримерvoid myFunction(const floatamp; args...)
?2. 1) Вот как
Ts...
создается новое в рекурсивном вызове: fromT, Ts...
. 2) Я не уверен, что вы подразумеваете под «вместо аргумента шаблона». Показанное вами объявление, по крайней мере, на C , недопустимо.3. Ах, хорошо, теперь это имеет смысл. Что касается 2) я имел в виду, как мне распаковать пакет параметров при объявлении функции, которая принимает
N
аргументы типаT
?4. Я думаю, вы спрашиваете о «расширении пакета». Найдите этот термин и посмотрите, отвечает ли он на ваш вопрос.
5. Я немного знаком с расширением пакета параметров, моя точка зрения заключалась в том, как настроить ваше решение для кодирования только параметра переменной длины, а не специализации шаблона класса
C
, чтобы я мог объявить функциюvoid someFunction(expand<5, base_t>... args)
или что-то подобное.
Ответ №2:
Одно из возможных решений — использование кортежей и вспомогательных шаблонов для сборки нужного класса. Немного дополнительного сахара может сделать результирующий синтаксис «более чистым», протестированный с помощью gcc 10.2:
#include <tuple>
#include <type_traits>
// Create std::tuple<T ...>, with T repeated N times.
template<int N, typename T>
struct tuple_list : tuple_list<N-1, T> {
typedef decltype(std::tuple_cat(std::declval<typename tuple_list<N-1, T>
::tuple_t>(),
std::declval<std::tuple<T> amp;amp;>())
) tuple_t;
};
template<typename T>
struct tuple_list<0, T> {
typedef std::tuple<> tuple_t;
};
template<typename ...> struct SomeOtherClass {};
// And now, replace `std::tuple` with SomeOtherClass
template<typename tuple_t> struct make_some_other_class;
template<typename ...Args>
struct make_some_other_class<std::tuple<Args...>> {
typedef SomeOtherClass<Args...> type_t;
};
template < int N >
class MyTemplatedType {
typedef float base_t;
public:
// The payload is not exactly "clean", but one more helper
// template can make the syntax here a little bit nicer...
typename make_some_other_class< typename tuple_list<N, base_t>
::tuple_t >::type_t foo;
};
void foobar(MyTemplatedType<3> amp;bar)
{
SomeOtherClass<float, float, float> amp;this_works=bar.foo;
}