Я читал о списках типов в "Современном дизайне C++" и понял, что это своего рода объединение типов. Помещая разные, несвязанные типы в список типов, можно использовать его для одновременного представления более одного типа без наследования. Я протестировал список типов в некоторых простых функциях с примитивными типами, но не смог заставить ни одну из них работать.
Может ли кто-нибудь сказать мне, правильно ли я понимаю списки типов, и привести простой пример из реальной жизни, как использовать списки типов в обычном повседневном коде? Заранее спасибо.
Кстати, я использую Windows и Visual Studio 2005 и его компилятор.
РЕДАКТИРОВАТЬ: мои примеры исчезли, я использую проект песочницы в 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";
}
Ответ 1 · рейтинг 20 · poliklosio
Списки типов являются универсальными коллекциями типов времени компиляции. Если вы используете dynamic_cast, вы упускаете смысл, потому что он не нужен, потому что это статическая концепция времени компиляции.
Это работает, но я не вижу преимущества перед наследованием, которое способно сделать то же самое.
Вы не можете заставить какой-либо существующий тип наследовать что-то, что вы хотите. Это просто невозможно, потому что этот существующий тип может быть встроенным типом или типом из библиотеки. Думайте о списках типов как о расширениях списков типов (например, в std::pair) для любого разумного количества типов (вместо двух).
Списки типов можно использовать для создания средства передачи набора аргументов функции. Это фрагмент кода, который вызывает обобщенные функторы с 5 параметрами (еще одна концепция из современного дизайна C++) с аргументами, предоставленными в кортеже (еще одном) со списком типов, который определяет типы объектов, хранящихся в кортеже:
//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 лет в проекте, и из-за кода шаблона (его много) размеры исполняемых файлов в версиях DEBUG, как правило, БОЛЬШИЕ (мой рекорд был 35 МБ или около того). Также был небольшой удар по скорости компиляции. Также помните, что C++0x, вероятно, будет включать некоторый эквивалентный механизм. Вывод: старайтесь не использовать списки типов, если в этом нет необходимости.
Ответ 2 · рейтинг 4 · Paul Hollingsworth
Списки типов - это способ передачи "списков параметров" метапрограммам шаблонов, которые "выполняются" как часть процесса компиляции.
Как таковые, их можно использовать для создания какого-либо типа "объединения", но это только одно из возможных применений.
Для "реального" примера: мы использовали списки типов как способ автоматической генерации метода "QueryInterface" при реализации COM-объектов в Comet.
Это позволило вам написать такой код:
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" был шаблоном, используемым для создания "списка типов", который затем шаблон внедрения_qi мог "перечислить" в генерировать соответствующий код QueryInterface.