13

「Modern C++ Design」で型リストについて読み、それが型のある種の共用体であると理解しました。関連性のない異なる型を型リストに入れることで、それを使用して、継承なしで一度に複数の型を表すことができます。プリミティブ型を持ついくつかの単純な関数で 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)の拡張と考えてください。

タイプリストを使用して、一連の引数を関数に渡す機能を作成できます。これは、タプルに保持されているオブジェクトのタイプを定義するタイプリストを使用して、タプ(さらに別の1つ)で提供される引数を使用して、5つのパラメーターの一般化されたファンクター(Modern 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);
}

タプルやファンクターのような他の概念でのみ、タイプリストが有用になり始めることに注意してください。また、私はプロジェクトで約2年間Lokiを経験しており、テンプレートコード(多くの場合)のために、DEBUGバージョンの実行可能ファイルのサイズは大きくなる傾向があります(私の記録は35 MB程度でした)。また、コンパイルの速度にも少し打撃がありました。また、C++0xにはおそらく同等のメカニズムが含まれていることを思い出してください。結論:必要がない場合は、タイプリストを使用しないようにしてください。

于 2009-05-24T02:33:39.627 に答える
4

タイプリストは、コンパイル プロセスの一部として「実行」されるテンプレート メタ プログラムに「パラメータのリスト」を渡す方法です。

そのため、ある種の「ユニオン」タイプを生成するために使用できますが、これは可能な用途の 1 つにすぎません。

「実世界」の例: タイプリストを使用して、 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 に答える