Как можно использовать параметры шаблона шаблона?
Я видел несколько примеров C++, использующих 9X_templates параметры шаблона шаблона (то есть шаблоны, которые 9X_template принимают шаблоны в качестве параметров) для 9X_cpp создания классов на основе политик. Как 9X_cxx еще можно использовать эту технику?
- Я пришел с другого направления (FP, ...
Ответ #1
Ответ на вопрос: Как можно использовать параметры шаблона шаблона?
Я думаю, вам нужно использовать синтаксис 9X_templates шаблона шаблона для передачи параметра, тип 9X_template которого является шаблоном, зависящим от 9X_template другого шаблона, например этого:
template
class H, class S> void f(const H &value) { }
Здесь H
- шаблон, но 9X_c++ я хотел, чтобы эта функция работала со всеми 9X_templates специализациями H
.
ПРИМЕЧАНИЕ. Я программировал на C++ много 9X_cxx лет, и мне это понадобилось только один 9X_cpp раз. Я считаю, что это редко используемая 9X_template-templates функция (конечно, удобная, когда она вам 9X_template-templates нужна!).
Я пытался придумать хорошие примеры, и, честно 9X_cpp говоря, в большинстве случаев в этом нет 9X_template необходимости, но давайте придумаем пример. Представим, что 9X_cxx std::vector
не имеет typedef value_type
.
Итак, как бы вы написали функцию, которая 9X_c++ может создавать переменные правильного типа 9X_c++ для векторных элементов? Это сработает.
template
class V, class T, class A> void f(V &v) { // This can be "typename V::value_type", // but we are pretending we don't have it T temp = v.back(); v.pop_back(); // Do some work on temp std::cout << temp << std::endl; }
ПРИМЕЧАНИЕ. std::vector
имеет 9X_templates два параметра шаблона, тип и распределитель, поэтому 9X_c++ нам пришлось принять их оба. К счастью, из-за 9X_cpp вывода типа нам не нужно явно указывать 9X_template-templates точный тип.
который можно использовать так:
f(v); // v is of type std::vector using any allocator
или 9X_templates еще лучше, мы можем просто использовать:
f(v); // everything is deduced, f can deal with a vector of any type!
ОБНОВЛЕНИЕ. Даже 9X_c++ этот надуманный пример, хотя и является 9X_template-templates иллюстративным, больше не является прекрасным 9X_template-templates из-за того, что в C++ 11 введен auto
. Теперь 9X_templates ту же функцию можно записать как:
template void f(Cont &v) { auto temp = v.back(); v.pop_back(); // Do some work on temp std::cout << temp << std::endl; }
именно 9X_templates так я бы предпочел написать этот тип кода.
Что ж, вам не нужно предоставлять распределитель. Важно то, что параметр шаблона шаблона был определен с правильным количеством аргументов. Но функция не должна заботиться об их «типах» или значении, в C++ ...
Ответ #2
Ответ на вопрос: Как можно использовать параметры шаблона шаблона?
На самом деле, вариант использования параметров 9X_templates шаблона достаточно очевиден. Как только 9X_c++ вы узнаете, что в C++ stdlib есть зияющая 9X_templates дыра в том, что не определены операторы 9X_c++ вывода потока для стандартных типов контейнеров, вы 9X_template-templates можете приступить к написанию чего-то вроде:
template static inline std::ostream& operator<<(std::ostream& out, std::list const& v) { out << '['; if (!v.empty()) { for (typename std::list::const_iterator i = v.begin(); ;) { out << *i; if (++i == v.end()) break; out << ", "; } } out << ']'; return out; }
Тогда 9X_cpp вы поймете, что код для вектора такой же, для 9X_cpp forward_list такой же, на самом деле, даже 9X_templates для множества типов карт он остается неизменным. Эти 9X_templates классы шаблонов не имеют ничего общего, кроме 9X_cxx метаинтерфейса / протокола, и использование 9X_template-templates параметра шаблона шаблона позволяет зафиксировать 9X_template-templates общность во всех из них. Однако прежде чем 9X_cpp приступить к написанию шаблона, стоит проверить 9X_cpp ссылку, чтобы вспомнить, что контейнеры 9X_cxx последовательности принимают 2 аргумента 9X_cxx шаблона - для типа значения и распределителя. Хотя 9X_c++ распределитель установлен по умолчанию, мы 9X_cpp все равно должны учитывать его существование 9X_cxx в нашем шаблоне operator <<:
template
class Container, class V, class A> std::ostream& operator<<(std::ostream& out, Container const& v) ...
Вуаля, это будет 9X_cpp работать автоматически для всех нынешних 9X_cxx и будущих контейнеров последовательностей, придерживающихся 9X_templates стандартного протокола. Чтобы добавить карты 9X_cxx в микс, нужно взглянуть на ссылку, чтобы 9X_cpp заметить, что они принимают 4 параметра 9X_template-templates шаблона, поэтому нам понадобится другая 9X_cxx версия оператора << выше с параметром шаблона 9X_template-templates шаблона с 4 аргументами. Мы также увидели 9X_cpp бы, что std: pair пытается отобразить с 9X_template-templates помощью 2-arg operator << для типов последовательностей, которые 9X_cxx мы определили ранее, поэтому мы предоставим 9X_templates специализацию только для std :: pair.
Между 9X_template-templates прочим, с C + 11, который допускает вариативные 9X_cxx шаблоны (и, следовательно, должен разрешать 9X_cpp вариативные аргументы шаблона шаблона), можно 9X_templates было бы иметь один оператор <<, чтобы управлять 9X_template ими всеми. Например:
#include #include #include #include template class C, class... Args> std::ostream& operator <<(std::ostream& os, const C& objs) { os << __PRETTY_FUNCTION__ << '\n'; for (auto const& obj : objs) os << obj << ' '; return os; } int main() { std::vector vf { 1.1, 2.2, 3.3, 4.4 }; std::cout << vf << '\n'; std::list lc { 'a', 'b', 'c', 'd' }; std::cout << lc << '\n'; std::deque di { 1, 2, 3, 4 }; std::cout << di << '\n'; return 0; }
Вывод
std::ostream &operator<<(std::ostream &, const C &) [T = float, C = vector, Args = >] 1.1 2.2 3.3 4.4 std::ostream &operator<<(std::ostream &, const C &) [T = char, C = list, Args = >] a b c d std::ostream &operator<<(std::ostream &, const C &) [T = int, C = deque, Args = >] 1 2 3 4
Это самый пробуждающий для меня ответ в шаблонах C++. @WhozCraig Как вы узнали ...
Ответ #3
Ответ на вопрос: Как можно использовать параметры шаблона шаблона?
Вот простой пример, взятый из 'Modern C++ Design - Generic Programming and Design Patterns Applied' Андрея Александреску:
Он 9X_template-templates использует классы с параметрами шаблона 9X_template шаблона для реализации шаблона политики:
// Library code template
class CreationPolicy> class WidgetManager : public CreationPolicy { ... };
Он 9X_template объясняет: Обычно класс хоста уже знает или может легко вывести аргумент шаблона класса политики. В приведенном выше примере WidgetManager всегда управляет объектами типа Widget, поэтому требование, чтобы пользователь снова указывал Widget при создании экземпляра CreationPolicy, является избыточным и потенциально опасным. В этом случае код библиотеки может использовать параметры шаблона шаблона для определения политик.
В результате клиентский код может 9X_templates использовать WidgetManager более элегантно:
typedef WidgetManager MyWidgetMgr;
Вместо 9X_cpp более громоздкого и подверженного ошибкам 9X_templates способа, который потребовалось бы для определения 9X_template-templates без аргументов шаблона шаблона:
typedef WidgetManager< MyCreationPolicy > MyWidgetMgr;
В вопросе конкретно запрашиваются примеры, отличные от шабло ...
Ответ #4
Ответ на вопрос: Как можно использовать параметры шаблона шаблона?
Вот еще один практический пример из моего 9X_template CUDA Convolutional neural network library. У меня есть следующий шаблон класса:
template class Tensor
, который 9X_template-templates на самом деле реализует манипуляции с n-мерными 9X_template-templates матрицами. Также существует шаблон дочернего 9X_template класса:
template class TensorGPU : public Tensor
, который реализует те же функции, но 9X_cpp в графическом процессоре. Оба шаблона могут 9X_cxx работать со всеми основными типами, такими 9X_template как float, double, int и т. Д. А еще у меня 9X_template-templates есть шаблон класса (упрощенный):
template
class TT, class T> class CLayerT: public Layer > { TT weights; TT inputs; TT connection_matrix; }
Причина, по 9X_templates которой здесь используется синтаксис шаблона 9X_c++ шаблона, заключается в том, что я могу объявить 9X_c++ реализацию класса
class CLayerCuda: public CLayerT
который будет иметь как 9X_template-templates веса, так и входные данные типа float и 9X_cpp на GPU, но connection_matrix всегда будет 9X_templates int, либо на CPU (указав TT = Tensor), либо 9X_c++ на GPU (указав TT = TensorGPU).
Ответ #5
Ответ на вопрос: Как можно использовать параметры шаблона шаблона?
Допустим, вы используете CRTP для предоставления 9X_template-templates «интерфейса» для набора дочерних шаблонов; и 9X_templates родительский, и дочерний элементы являются 9X_template-templates параметрическими в другом (-ых) аргументе 9X_cxx (-ах) шаблона:
template class interface { void do_something(VALUE v) { static_cast(this)->do_something(v); } }; template class derived : public interface { void do_something(VALUE v) { ... } }; typedef interface, int> derived_t;
Обратите внимание на дублирование 9X_template int, которое на самом деле является параметром 9X_template одного и того же типа, указанным для обоих 9X_templates шаблонов. Вы можете использовать шаблон 9X_cpp шаблона для DERIVED, чтобы избежать этого 9X_template дублирования:
template
class DERIVED, typename VALUE> class interface { void do_something(VALUE v) { static_cast*>(this)->do_something(v); } }; template class derived : public interface { void do_something(VALUE v) { ... } }; typedef interface derived_t;
Обратите внимание, что вы исключаете 9X_c++ прямую передачу других параметров шаблона 9X_template-templates производному шаблону; "интерфейс" по-прежнему их получает.
Это 9X_template-templates также позволяет вам создавать определения 9X_cpp типов в «интерфейсе», которые зависят от 9X_cxx параметров типа, которые будут доступны 9X_template-templates из производного шаблона.
Приведенный выше 9X_cxx typedef не работает, потому что вы не можете 9X_template ввести typedef для неопределенного шаблона. Однако 9X_template это работает (а в C++ 11 есть встроенная 9X_c++ поддержка шаблонов typedefs):
template struct derived_interface_type { typedef typename interface type; }; typedef typename derived_interface_type::type derived_t;
Вам понадобится 9X_template один производный_интерфейс_тип для каждого 9X_template-templates экземпляра производного шаблона, к сожалению, если 9X_cxx нет другого трюка, которому я еще не научился.
Ответ #6
Ответ на вопрос: Как можно использовать параметры шаблона шаблона?
Вот с чем я столкнулся:
template class B { A& a; }; template class A { B b; }; class AInstance : A>>>>>>> { };
Можно решить, чтобы:
template class B { A& a; }; template< template class B> class A { B b; }; class AInstance : A //happy { };
или 9X_template (рабочий код):
template class B { public: A* a; int GetInt() { return a->dummy; } }; template< template class B> class A { public: A() : dummy(3) { b.a = this; } B b; int dummy; }; class AInstance : public A //happy { public: void Print() { std::cout << b.GetInt(); } }; int main() { std::cout << "hello"; AInstance test; test.Print(); }
Ответ #7
Ответ на вопрос: Как можно использовать параметры шаблона шаблона?
Вот одно обобщение из того, что я только 9X_templates что использовал. Я публикую его, так как 9X_cxx это очень простой пример, и он демонстрирует 9X_cxx практический вариант использования вместе 9X_c++ с аргументами по умолчанию:
#include template class Alloc final { /*...*/ }; template
class allocator=Alloc> class MyClass final { public: std::vector> field0; std::vector> field1; };
Ответ #8
Ответ на вопрос: Как можно использовать параметры шаблона шаблона?
В решении с вариативными шаблонами, предоставленным 9X_cpp pfalcon, мне было трудно на самом деле специализировать 9X_template оператор ostream для std :: map из-за жадной 9X_templates природы вариативной специализации. Вот небольшая 9X_template доработка, которая у меня сработала:
#include #include #include #include #include
Ответ #9
Ответ на вопрос: Как можно использовать параметры шаблона шаблона?
Это улучшает читаемость вашего кода, обеспечивает 9X_c++ дополнительную безопасность типов и экономит 9X_templates некоторые усилия компилятора.
Допустим, вы 9X_template хотите распечатать каждый элемент контейнера, вы 9X_templates можете использовать следующий код без параметра 9X_cpp шаблона шаблона
template void print_container(const T& c) { for (const auto& v : c) { std::cout << v << ' '; } std::cout << '\n'; }
или с параметром шаблона 9X_template шаблона
template< template class ContainerType, typename ValueType, typename AllocType> void print_container(const ContainerType& c) { for (const auto& v : c) { std::cout << v << ' '; } std::cout << '\n'; }
Предположим, вы передаете целое число, например 9X_templates print_container(3)
. В первом случае экземпляр шаблона будет 9X_c++ создан компилятором, который будет жаловаться 9X_cpp на использование c
в цикле for, последний 9X_cpp не будет создавать экземпляр шаблона вообще, поскольку 9X_cxx не может быть найден подходящий тип.
Вообще 9X_cxx говоря, если ваш класс / функция шаблона 9X_c++ предназначен для обработки класса шаблона 9X_templates как параметра шаблона, лучше прояснить это.
Ответ #10
Ответ на вопрос: Как можно использовать параметры шаблона шаблона?
Я использую его для версионных типов.
Если 9X_template у вас есть тип, версионированный с помощью 9X_cxx шаблона, такого как MyType
, вы можете написать 9X_cxx функцию, в которой вы можете фиксировать 9X_cxx номер версии:
template
T, uint8_t Version> Foo(const T& obj) { assert(Version > 2 && "Versions older than 2 are no longer handled"); ... switch (Version) { ... } }
Таким образом, вы можете делать 9X_templates разные вещи в зависимости от версии передаваемого 9X_c++ типа вместо того, чтобы иметь перегрузку 9X_template для каждого типа. У вас также могут быть 9X_cxx функции преобразования, которые принимают 9X_template-templates MyType
и возвращают MyType
общим способом и даже рекурсивно 9X_c++ рекурсируют их, чтобы иметь функцию ToNewest()
, которая 9X_cpp возвращает последнюю версию типа из любой 9X_template-templates более старой версии (очень полезно для журналы, которые 9X_templates могли быть сохранены некоторое время назад, но 9X_cxx должны обрабатываться с помощью новейшего 9X_cxx инструмента сегодняшнего дня).
-
3 -
11 -
11 -
27 -
17 -
8 -
12 -
9 -
9 -
5 -
7 -
23 -
13 -
20 -
9 -
28 -
5 -
5 -
2 -
2 -
15 -
4 -
2 -
8 -
8 -
8 -
4 -
4 -
9 -
7 -
3 -
13 -
8 -
2 -
4 -
4 -
10 -
8 -
3 -
3 -
8 -
4 -
9 -
11 -
9 -
2 -
3 -
3 -
3 -
6