Существует общий метод для "разнесения" типа, чтобы проверить, был ли он создан шаблоном, и для извлечения типов, которые были переданы этому шаблону.Также вы можете получить доступ к самому шаблону и передать ему другие параметры, если хотите.
vector
- это шаблон класса .Когда вы применяете к нему параметры, вы получаете что-то вроде vector<int>
, то есть шаблон класса . шаблонный класс - это особый тип, как и любой другой тип, просто он создан с помощью шаблона класса.
Цель, учитывая тип T
, проверитьесли это шаблон класса , и если это так, чтобы получить доступ к шаблону класса , который использовался для его создания, а также для доступа к параметрам, которые были переданы в шаблон класса .В этом примере я просто проверяю, является ли что-то шаблоном с одним аргументом или с двумя аргументами, но метод может быть легко расширен.
(Технически, vector
- это шаблон с двумя аргументами.значение по умолчанию для второго параметра, поэтому vector<int>
на самом деле vector<int, allocator<int> >
, но это все же в основном шаблон с двумя аргументами, а не шаблон с одним аргументом.)
Лучшее место для начала - это пример кода, который я поставил на ideone .Я скопирую код Exploder в конце этого ответа.
Я начинаю с
typedef list<int> list_of_ints;
и продолжаю использовать шаблон Exploder
для доступа ко всей вышеуказанной информации.Например, Exploder<list_of_ints> :: type_1
- это первый параметр, который был передан в шаблон, в данном случае int
.Второй параметр (это параметр по умолчанию) равен allocator<int>
и доступен с Exploder<list_of_ints> :: type_2
.
typedef Exploder<list_of_ints> :: type_2 should_be_an_allocator_int;
Учитывая этот второй тип, который, как мы знаем, был создан шаблоном, мы можем получить доступ к его параметрувведите int
с Exploder< should_be_an_allocator_int > :: type_1
, но более интересно получить доступ к шаблону allocator
и передать ему другой параметр.Следующая строка оценивает, в этом примере, allocator<double>
.
typedef Exploder< should_be_an_allocator_int >
:: rebind<double> :: type should_be_an_allocator_double;
Таким образом, даже если ваш тип list<...,...>
не не использовал распределитель по умолчанию, вы можете получить доступ киспользованный распределитель, а также любой шаблон класса , использованный для создания типа распределителя.
Теперь, когда у нас есть подходящий распределитель, мы можем вернуться к нашему исходному шаблону класс list<int>
и замените int
на double
:
Exploder<list_of_ints> :: rebind<double, should_be_an_allocator_double> :: type
Чтобы убедиться, что все это работает, в примере кода используется typeid(...).name()
для печати фактического типа различных объектов,вместе с правильным типом, который должен быть.Вы можете видеть, что они совпадают.
(Кроме того, некоторые шаблоны таковы, что их параметры являются не типами, а другими шаблонами классов или даже другими шаблонами шаблонов .Должна быть возможность извлечь все это, но я не буду вдаваться в подробности.)
(Последнее интересное техническое примечание. Некоторые типы, такие как allocator
, имеют нечто, называемое rebind
чтобы разрешить такого рода доступ. Но техника, использованная выше, работает для всех шаблонных классов, даже для тех, у кого нет rebind
)
Полный код для шаблона Exploder
См. Пример кода, который я поставил на ideone для полной демонстрации.
template <class>
struct Exploder;
template<class T, template<class> class Template>
struct Exploder< Template<T> > {
static const char * description() { return " One-arg template. Arg 1 is a type "; }
typedef T type_1;
template <class V>
struct rebind {
typedef Template<V> type;
};
};
template<class T, class U, template<class,class> class Template>
struct Exploder< Template<T,U> > {
static const char * description() { return " Two-arg template. All args are types, as opposed to being (unapplied) templates. "; }
typedef T type_1;
typedef U type_2;
template <class V,class W>
struct rebind {
typedef Template<V,W> type;
};
};
template<class S, class T, class U, template<class,class,class> class Template>
struct Exploder< Template<S,T,U> > {
static const char * description() { return " Three-arg template. All args are types, as opposed to being (unapplied) templates. "; }
typedef S type_1;
typedef T type_2;
typedef U type_3;
};