13

我在“现代 C++ 设计”中阅读了类型列表,并将其理解为某种类型的联合。通过将不同的、不相关的类型放入类型列表中,可以使用它一次表示多个类型,而无需继承。我在一些具有原始类型的简单函数中测试了 typelist,但我无法让它们中的任何一个工作。

有人能告诉我我对类型列表的理解是否正确,并给出一个简单的现实世界示例如何在日常平均代码中使用类型列表吗?提前致谢。

顺便说一句,我正在使用 Windows 和 Visual Studio 2005 及其编译器。

编辑:我的例子不见了,我在 vs 中使用了一个沙箱项目来测试这些东西。但它很安静,类似于 Dobbs 教程中的代码:

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";
}
4

2 回答 2

20

类型列表是类型的通用编译时集合。如果你使用dynamic_cast,你就错过了重点,因为它不应该被需要,因为它是一个静态的、编译时的概念。

这行得通,但我没有看到能够做同样事情的继承的优势。

你不能让任何现有的类型继承你想要的任何东西。这根本不可行,因为这种现有类型可能是内置类型或库中的类型。将类型列表视为任何合理数量的类型(而不仅仅是 2 个)的类型列表(例如在 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 MB 左右)。编译速度也受到了一些影响。还记得 C++0x 可能会包含一些等效机制。结论:如果没有必要,尽量不要使用类型列表。

于 2009-05-24T02:33:39.627 回答
4

类型列表是一种将“参数列表”传递给作为编译过程的一部分“执行”的模板元程序的方式。

因此,它们可用于生成某种“联合”类型,但这只是一种可能的用途。

对于“真实世界”的示例:我们使用类型列表作为在Comet库中实现 COM 对象时自动生成“QueryInterface”方法的一种方式。

它允许您编写如下代码:

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 模板随后可以“枚举”以生成适当的 QueryInterface 代码

于 2009-05-23T17:14:17.143 回答