0

std のようなインターフェイスを使用してデータ構造クラスを構築し、データ構造にさまざまな反復子を実装しています。

概念的には、私がやりたいことは次のようなものです。

template <class DataT>
class DataStructure
{
protected:
    DataT& Data;
public:
    DataStructure(DataT& data) : Data(data) {}
    class BaseIterator
    {
    public:
        BaseIterator()
        {
            cout<<"BaseIterator"<<endl;
        }
    };

    class DerrivedIterator1 : public BaseIterator
    {
    public:
        DerrivedIterator1()
        {
            cout<<"DerrivedIterator1"<<endl;
        }
    };

    class DerrivedIterator2 : public BaseIterator
    {
    public:
        DerrivedIterator2()
        {
            cout<<"DerrivedIterator2"<<endl;
        }
    };

    template<class IterT>
    IterT Begin()
    {
        //none-specialized implementation. Possibly throw exception
    }

    template<>
    DerrivedIterator1 Begin<DerrivedIterator1>()
    {
        //Find beginning for DerrivedIterator1
    }

    template<>
    DerrivedIterator2 Begin<DerrivedIterator2>()
    {
        //Find beginning for DerrivedIterator1
    }
};

ただし、これはもちろんコンパイルされません。C++ では、特殊化されていないテンプレート コンテナーでテンプレート メンバー関数を特殊化することが許可されていないためです。

明らかな回避策は、もちろん、Begin_Iterator1 と Begin_Iterator2 の 2 つの異なる関数を宣言し、それを実行することです。しかし、インターフェイスを変更しない回避策を探しています。

何か案は?

編集:これはHW割り当てのためであることを忘れていたので、boostやstdでさえオプションではありません。

4

2 に答える 2

1

関数テンプレートを C++ に特化することはできません。

それらがテンプレートのメンバーであるかどうかは関係ありません。関数テンプレートの特殊化は許可されていません。通常、引数の型を使用してテンプレート引数を推測する場合、オーバーロードは同じ特殊化を行うため、関数の特殊化 (およびオーバーロード解決などに関連する余分な複雑さ) は必要とは見なされませんでした。

ただし、推論する引数がなく、テンプレートを手動でインスタンス化します。いいえ、

DataStructure::DerivedIterator1 i = dataStructure.Begin();

型推論は、オーバーロードの解決が引数に対してのみ行われ、期待される戻り値では行われないためです。あなたは書く必要があります:

DataStructure::DerivedIterator1 i = dataStructure.Begin<DataStructure::DerivedIterator1>();

そして、それは以下よりもメリットがありません:

DataStructure::DerivedIterator1 i = dataStructure.BeginIterator1();

ただし、最初の式は、いくつかの魔法で機能するように作成できます。最初に定義する必要がBeginIterator1ありBeginIterator2、構築するものを後で決定するために一時的に行うよりも、次のようにします。

class DataStructure {
    ...
    class BeginIteratorConstructor {
        DataStructure &dataStructure;
    public:
        BeginIteratorConstructor(DataStructure &ds) : dataStructure(ds) {}
        operator DerivedIterator1() { return dataStructure.BeginIterator1(); }
        operator DerivedIterator2() { return dataStructure.BeginIterator2(); }
    };
    BeginIteratorConstructor Begin() { return BeginIteratorConstructor(*this); }
    ...
};

にキャストするdataStructure.Begin()と呼び出される一時的な何かを返すようになり、 にキャストすると呼び出されるようになりました。コンパイラがどちらにキャストするかを決定できない場所に渡すと、あいまいなオーバーロードのため、または実際にはイテレータではないため、明示的にキャストする必要があります。BeginIterator1DerivedIterator1BeginIterator2DerivedIterator2BeginIteratorConstructor

(できるだけ多くのBeginIteratorConstructorプライベートを慎重に作成する必要がありますが、コンパイラーがどこまで許可するかはわかりません。そのため、少し実験する必要があります)

于 2011-04-26T13:10:33.193 に答える
0

タグ付けシステムを使用すると、クラス テンプレート内の部分的に特殊化された関数から解放されます。

struct base_iter_tag{};
struct der1_iter_tag{};
struct der2_iter_tag{};

template<class T>
struct iter_type;

template<>
struct iter_type<BaseIterator>{
  typedef base_iter_tag tag;
};

template<>
struct iter_type<DerivedIterator1>{
  typedef der1_iter_tag tag;
};

template<>
struct iter_type<DerivedIterator2>{
  typedef der2_iter_tag tag;
};

template<class IterT>
IterT Begin(){
  return DoBegin(typename iter_type<IterT>::tag());
}

BaseIterator DoBegin(base_iter_tag){
  // ...
}

DerivedIterator1 DoBegin(der1_iter_tag){
  // ...
}

DerivedIterator2 DoBegin(der2_iter_tag){
  // ...
}

これは基本的に、標準ライブラリがiterator_traits<T>::iterator_categoryカテゴリに応じて および オーバーロードされた関数で行うことです (例: forward_iterator_tagrandom_access_iterator_tagなど)。

于 2011-04-26T13:14:02.180 に答える