1

私はアプリケーションの設計を開発しており、ある種のVisitor デザイン パターンを適用できると思っていましたが、それはまさに私が探しているものではないことが判明しました。この場合、誰かが私が必要とするバリアントを教えてくれるでしょうか?

私のコードの多くには、次のようなテンプレート引数「ContainerType」があります

template <class ContainerType>
class MyClass
{
public:
  void doSomething(ContainerType& container) { ... }
};

現在、通常は多くのデータ フィールドを共有する「コンテナ」の数は少ないですが増え続けています。

template<class ContainedType>
struct ContainerBase 
{ 
  ContainedType data;
};

struct Container1: ContainerBase<A>, ContainerBase<B>
{};
struct Container2: ContainerBase<A>, ContainerBase<C>
{};

Container1 と Container2 は、MyClass (およびその他) のテンプレート引数として使用されるようになりました。ここで、A、B、C はいくつかの定義済みクラスです。(get<A>(container)含まれているデータにアクセスするなどの方法があります。この設計により、必要な型を含むすべてのコンテナー型で MyClass を使用できるというコンパイル時の安全性が提供されます。)

ここで、「コンテナに特定のタイプ (例: A) が含まれている場合は何かを実行し、それ以外の場合は何もしない」という機能を追加したいと考えています。

これは、訪問者のように見えるもので行うことができます (ただし、仮想メソッドは使用されないことに注意してください)。「コンテナに A が含まれている場合はこれを行い、D が含まれている場合は別のことを行い、そうでない場合は何もしない」こともできます。これは

template <class ContainerType>
class MyClass
{
public:
    void doSomething(ContainerType& container) 
    { 
        container.accept(*this); 
    }

    void visit(B& b){...}
    void visit(D& d){...}

    template<typename T>
    void visit(T& t){}
};


struct Container1: ContainerBase<A>, ContainerBase<B>
{
    template<class T>
    void accept(T& t)
    {
        t.visit(ContainerBase<A>::data);
        t.visit(ContainerBase<B>::data);
    }
};

これは私が望んでいたことですが、ここに示す実装では、すべての ContainerType に対して受け入れを実装する必要があるため、より良い方法を探しています。誰かがacceptメソッドから派生しContainer1ContainerBase<D>のに展開するのを忘れた場合、事態は悪化します。さらに悪いことに、const バージョンと非 const バージョンの accept が必要になり、一部のコンテナーには 5 つ以上の型が含まれているため、見た目も良くありません。

すべてのコンテナ クラスは複数回の継承によって構築されているContainerBase<T>ため、この構造を使用して、ContainerBase クラスに accept (および accept(..) const) を実装できないか考えてみました。私はすでに Lokis のタイプリストを見てきましたが、ここでそれらを使用する方法がわかりません。何か考えはありますか?

それとも、訪問者のような構造なしでこれを行うことは可能ですか?

どうもありがとう!

編集:RTTIを使用できることはわかっていますが、可能であればランタイムチェックと仮想メソッドを避けたいです。

4

3 に答える 3

3

コンテナ クラスの定義方法を変更できれば、Boost.Fusion で簡単に実現できそうです。

例えば

#include <iostream>

#include <boost/fusion/container/vector.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>

struct A {};
struct B {};
struct C {};

namespace fusion = boost::fusion;

struct Container1 : fusion::vector< A, B > {};

struct Container2 : fusion::vector< A, C > {};

struct doSomethingWithData {
    void operator()( B &b ) const
    {
        std::cout << "do something with B" << std::endl;
    }

    void operator()( C &c ) const
    {
        std::cout << "do something with C" << std::endl;
    }

    template < typename T >
    void operator()( T &t ) const
    {
        std::cout << "Neither B nor C" << std::endl;
    }
};

template < typename ContainerType >
void doSomething( ContainerType &container )
{
    fusion::for_each( container, doSomethingWithData() );
}

int main()
{
    Container1 c1;
    doSomething( c1 );
    std::cout << "----------------------" << std::endl;
    Container2 c2;
    doSomething( c2 );
}
于 2010-10-09T16:34:26.527 に答える
1

boost :: mplを使用して、含まれているタイプのタイプリストを次のように定義できます。

typedef boost::mpl::vector<A, B, C> ContainedTypes;

boost :: mpl :: for_eachを使用すると、これらの含まれるタイプごとにファンクターを呼び出すことができます。

例えば

template<class U>
class Visitor
{
public:
  Visitor(MyClass<U>& item) : _item(item)
{}

template<class T>
void operator() (T)
{
  // Do what ever you want, this may be specialized as needed
}

private:
  MyClass<U>& item;
}

その後、電話

boost::mpl::for_each<ContainedTypes> (Visitor(MyClass<ContainerType>& item))

これにより、ContainedTypesの各クラスに対してVisitor()のoperator()が呼び出されます。特殊化を使用したこのアプローチの欠点は、TとUの組み合わせに対してoperator()を特殊化する必要があることです。

お役に立てれば、

マーティン

于 2010-10-09T09:41:35.007 に答える
0

この場合に必要なバリアントはBoost.Variantである可能性があります:-)

于 2010-10-09T08:03:15.513 に答える