Как использовать списки типов
-
05-09-2019 - |
Вопрос
Я читал о списках типов в "Современном дизайне C ++" и понял это как своего рода объединение типов.Помещая различные, не связанные типы в список типов, можно использовать его для представления более чем одного типа одновременно, без наследования.Я протестировал typelist в некоторых простых функциях с примитивными типами, но не смог заставить ни одну из них работать.
Может кто-нибудь сказать мне, верно ли мое непонимание списков типов, и привести простой реальный пример того, как использовать списки типов в повседневном среднем коде?Заранее благодарю.
Кстати, я использую Windows и Visual Studio 2005 и его компилятор.
Редактировать:мои примеры исчезли, я использую проект sandbox в vs для тестирования этих вещей.Но это было тихо, похоже на код в руководстве Доббса:
void SomeOperation(DocumentItem* p)
{
if (TextArea* pTextArea = dynamic_cast<TextArea*>(p))
{
... operate on a TextArea object ...
}
else if (VectorGraphics* pVectorGraphics =
dynamic_cast<VectorGraphics*>(p))
{
... operate on a VectorGraphics object ...
}
else if (Bitmap* pBitmap = dynamic_cast<Bitmap*>(p))
{
... operate on a Bitmap object ...
}
else
{
throw "Unknown type passed";
}
}
Это работает, но я не вижу преимущества перед наследованием, которое способно сделать то же самое.А динамическое приведение не работает с примитивными типами.Можно ли использовать его в качестве возвращаемого значения, например:
typedef Typelist<int, string> mylist
mylist myfunction() {
if(foo == bar)
return 5;
return "five";
}
Решение
Списки типов представляют собой общие коллекции типов во время компиляции.Если вы используете dynamic_cast, вы упускаете суть, потому что это не должно быть необходимо, потому что это статическая концепция времени компиляции.
Это работает, но я не вижу преимущества перед наследованием, которое способно сделать то же самое.
Вы не можете заставить какой-либо существующий тип наследовать от всего, что вы хотите.Это просто невозможно, потому что этот существующий тип может быть встроенным типом или типом из библиотеки.Думайте о списках типов как о расширениях списков типов (напримерв std::pair) для любого разумного числа типов (вместо всего 2).
Списки типов можно использовать для создания средства передачи набора аргументов функции.Это фрагмент кода, который вызывает обобщенные функторы из 5 параметров (еще одна концепция из современного дизайна C ++) с аргументами, предоставленными в tuple (еще одном) со списком типов, который определяет типы объектов, содержащихся в кортеже:
//functor is just a holder of a pointer to method and a pointer to object to call this
//method on; (in case you are unfamiliar with a concept)
template<class R, class t0, class t1, class t2, class t3, class t4>
R call(Loki::Functor<R,LOKI_TYPELIST_5(t0, t1, t2, t3, t4
)> func,
Loki::Tuple<LOKI_TYPELIST_5(t0, t1, t2, t3, t4)> tuple)
{
///note how you access fields
return func(Loki::Field<0>(tuple), Loki::Field<1>(tuple),
Loki::Field<2>(tuple), Loki::Field<3>(tuple),
Loki::Field<4>(tuple));
}
//this uses the example code
#include<iostream>
using namespace std;
int foo(ostream* c,int h,float z, string s,int g)
{
(*c)<<h<<z<<s<<g<<endl;
return h+1
}
int main(int argc,char**argv)
{
Loki::Functor<int,LOKI_TYPELIST_5(ostream*, int, float, string, int)> f=foo;
//(...)
//pass functor f around
//(...)
//create a set of arguments
Loki::Tuple<LOKI_TYPELIST_5(ostream*, int, float, string, int)> tu;
Field<0>(tu)=&cout;
Field<1>(tu)=5;
Field<2>(tu)=0.9;
Field<3>(tu)=string("blahblah");
Field<4>(tu)=77;
//(...)
//pass tuple tu around, possibly save it in a data structure or make many
//specialized copies of it, or just create a memento of a call, such that
//you can make "undo" in your application; note that without the typelist
//you would need to create a struct type to store any set of arguments;
//(...)
//call functor f with the tuple tu
call(f,tu);
}
Обратите внимание, что только с другими понятиями, такими как кортежи или функторы, списки типов начинают быть полезными.Кроме того, я использую Loki около 2 лет в проекте, и из-за шаблонного кода (его много) размеры исполняемых файлов в отладочных версиях, как правило, БОЛЬШИЕ (моя запись составляла 35 МБ или около того).Также был небольшой удар по скорости компиляции.Также напомним, что C ++ 0x, вероятно, будет включать какой-то эквивалентный механизм.Заключение:старайтесь не использовать списки типов, если в этом нет необходимости.
Другие советы
Списки типов - это способ передачи "списков параметров" шаблонным метапрограммам, которые "выполняются" как часть процесса компиляции.
Как таковые, они могут быть использованы для создания какого-то типа "объединение", но это только одно из возможных применений.
Для примера из "реального мира":Мы использовали typelists как способ автоматической генерации метода "QueryInterface" при реализации COM-объектов в Комета библиотека.
Это позволило вам написать подобный код:
class Dog : public implement_qi<make_list<IAnimal, INoisy, IPersistStream> >
{
// The implement_qi template has provided
// an implementation of COM's QueryInterface method for us without
// having to write an ugly ATL "message map" or use any Macros.
...
}
В этом примере "make_list" был шаблоном, используемым для создания "списка типов", который шаблон implement_qi затем мог "перечислять" в сгенерируйте соответствующий код интерфейса запроса.