Как использовать списки типов

Я читал о списках типов в «Современном дизайне 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";
}

person DaClown    schedule 23.05.2009    source источник
comment
Добавьте к вашему вопросу пример кода, который не работает.   -  person    schedule 23.05.2009


Ответы (2)


Списки типов являются универсальными коллекциями типов времени компиляции. Если вы используете 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, вероятно, будет включать некоторый эквивалентный механизм. Вывод: старайтесь не использовать списки типов, если в этом нет необходимости.

person poliklosio    schedule 24.05.2009
comment
Большое спасибо, вы кое-что прояснили для меня. Я думаю, что я должен прочитать главу еще раз. - person DaClown; 24.05.2009

Списки типов — это способ передачи «списков параметров» метапрограммам шаблонов, которые «выполняются» как часть процесса компиляции.

Как таковые, их можно использовать для создания какого-либо типа «объединения», но это только одно из возможных применений.

Для «реального» примера: мы использовали списки типов как способ автоматической генерации метода «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.

person Paul Hollingsworth    schedule 23.05.2009