5

私は現在、自分では解決できない問題に直面しています。基本的に私がやろうとしているのは、C++ で linq のような動作を実装することです。

ヘッダーのコードから始めます。

template<typename T, template<class = T> class A,
         template<class = T, template<class=T> class = A> class C>
class queryable
{
public:
    typedef T value_type;
    typedef A<value_type> allocator_type;
    typedef C<value_type, allocator_type> container_type;    // (1)
    typedef queryable<T, A, C> type;
    queryable(container_type const &) { }
    template<typename _Out> queryable<_Out, A, C> select(/* some delegate */);
    // more methods etc
}

そして、これは私がそれをインスタンス化する方法です:

std::vector<int> my_vec;
queryable<std::vector<int> > q(my_vec);

言うまでもなく、これは機能しません(そうでなければ、私はここにいません:))

さらに奇妙なのは、これでも機能しないように見えることです。

std::vector<int> my_vec;
queryable<int, std::allocator, std::vector> q(my_vec);

ご覧のとおり (選択関数を見てください)、次のようなものだけを使用しないことが重要です。

template<typename T> class queryable;

これを解決する方法について何か提案はありますか? そして、これは可能ですか?

どんな助けでも大歓迎です!

編集:私が得ているエラー:

../entry.cpp:19:58: error: type/value mismatch at argument 3 in template parameter list for ‘template<class T, template<class> class A, template<class, template<class> class<template-parameter-2-2> > class C> class failproof::collections::queryable’
../entry.cpp:19:58: error:   expected a template of type ‘template<class, template<class> class<template-parameter-2-2> > class C’, got ‘template<class _Tp, class _Alloc> class std::vector’
../entry.cpp:19:61: error: invalid type in declaration before ‘;’ token

編集2:

私が理解している限り、コンパイラーは、C が 2 つのクラス引数をとらず、1 つのクラス引数と 1 つのテンプレート化されたクラス引数 (1) をとらないことに不平を言っています。これは、C をそのように定義したためです。この問題を解決する方法はありますか?

4

3 に答える 3

7

テンプレートによって作成されたかどうかをテストするために型を「展開」し、そのテンプレートに渡された型を抽出する一般的な方法があります。テンプレート自体にアクセスして、必要に応じて他のパラメーターを渡すこともできます。

vectorクラステンプレートです。それにパラメーターを適用すると、テンプレート クラスvector<int>である のようなものが得られます。テンプレート クラスは特定の型であり、他の型と同様に、たまたまクラス テンプレートを介して作成されたものです。

目標は、 type を指定して、それがテンプレート クラスTであるかどうかをテストし、そうである場合は、それを作成するために使用されたクラス テンプレートにアクセスし、クラステンプレートに渡されたパラメーターにアクセスすることです。このサンプルでは、​​1 つの引数のテンプレートか 2 つの引数のテンプレートかをテストするだけですが、この手法は簡単に拡張できます。

(技術的にvectorは、 は引数が 2 つのテンプレートです。2 番目のパラメーターにはデフォルトがあり、vector<int>実際vector<int, allocator<int> >には です。ただし、基本的には引数が 1 つのテンプレートではなく、引数が 2 つのテンプレートです。)

開始するのに最適な場所は、私が ideone に置いたこのサンプル コードです。この回答の最後に Exploder コードをコピーします。

私はから始めます

typedef list<int> list_of_ints;

Exploderテンプレートを使用して上記のすべての情報にアクセスします。たとえば、Exploder<list_of_ints> :: type_1はテンプレートに渡された最初のパラメータです。この場合はintです。2 番目のパラメーター (これはデフォルトのパラメーターです) はallocator<int>であり、 でアクセスできますExploder<list_of_ints> :: type_2

typedef Exploder<list_of_ints> :: type_2  should_be_an_allocator_int;

テンプレートによって作成されたことがわかっているこの 2 番目の型を考えると、 を使用してそのパラメーター型 にアクセスできますがint、代わりExploder< should_be_an_allocator_int > :: type_1に実際にテンプレートにアクセスして別のパラメーターを渡す方が興味深いです。allocatorこの次の行は、この例ではallocator<double>.

typedef Exploder< should_be_an_allocator_int >
           :: rebind<double> :: type should_be_an_allocator_double;

したがって、list<...,...>型がデフォルトのアロケーターを使用していない場合でも、使用されたアロケーターにアクセスでき、アロケーター型の作成に使用されたクラス テンプレートにもアクセスできます。

適切なアロケータができたので、元のテンプレート クラス に戻って次のようlist<int>に置き換えます。intdouble

Exploder<list_of_ints> :: rebind<double, should_be_an_allocator_double> :: type

これがすべて機能していることを確認するために、サンプル コードtypeid(...).name()では、さまざまなオブジェクトの実際の型と、本来あるべき正しい型を出力します。一致していることがわかります。

(また、一部のテンプレートは、パラメータが型ではなく、他のクラスのテンプレート、または他のテンプレートのテンプレートでさえあるようなものです。それをすべて抽出できるはずですが、ここでは調べません。)

(最後にもう 1 つ興味深い技術的な注意事項があります。 などの一部の型には、この種のアクセスを許可するためにallocator呼び出されるものrebindがあります。ただし、上記で使用した手法は、独自の を持たないものであっても、すべてのテンプレート クラスで機能しますrebind)

テンプレート Exploder の完全なコード

完全なデモについては、ideone に追加したサンプル コードを参照してください。

template <class>
struct Exploder;

template<class T, template<class> class Template>
struct Exploder< Template<T> > {
        static const char * description() { return " One-arg template. Arg 1 is a type "; }
        typedef T type_1;
        template <class V>
        struct rebind {
                typedef Template<V> type;
        };
};
template<class T, class U, template<class,class> class Template>
struct Exploder< Template<T,U> > {
        static const char * description() { return " Two-arg template. All args are types, as opposed to being (unapplied) templates. "; }
        typedef T type_1;
        typedef U type_2;
        template <class V,class W>
        struct rebind {
                typedef Template<V,W> type;
        };
};
template<class S, class T, class U, template<class,class,class> class Template>
struct Exploder< Template<S,T,U> > {
        static const char * description() { return " Three-arg template. All args are types, as opposed to being (unapplied) templates. "; }
        typedef S type_1;
        typedef T type_2;
        typedef U type_3;
};
于 2012-01-18T21:40:32.517 に答える
6

標準コンテナ(アロケータ)の2番目のテンプレートパラメータはであり、テンプレートではないため、3番目のパラメータを次のように変更する必要があります。

template<typename, typename> class C

(テンプレートパラメータ仕様のデフォルトの引数は何の役にも立たないので、ここでは省略していることに注意してください。)

次に、テンプレートを次のようにインスタンス化できるはずです。

queryable<int, std::allocator, std::vector>

value_typeコンテナタイプをパラメータ化してから、その定義とallocator_type定義を使用する方がよい場合があります。

template <typename C> class queryable
{
public:
    typedef typename C::value_type value_type;
    typedef typename C::allocator_type allocator_type;
    typedef C container_type;
};

(1つの欠点は、アロケータテンプレートに直接アクセスできないことです。ただし、他のタイプのテンプレートをインスタンス化する必要がある場合は、アロケータタイプのネストされた定義を使用できます。)rebind

また、typedef iterator const const_iterator;間違っています。これは、シーケンスの変更に使用できる変更不可能なイテレーターを宣言しますが、シーケンスの変更には使用できない変更可能なイテレーターであると想定されています。const_iterator

于 2012-01-18T13:01:22.147 に答える
1

(用語に関する注意.vectorクラス テンプレートです。つまり、パラメータはありません。そしてvector<int>テンプレート クラスです。つまり、テンプレートによって作成されたことを除いて、他のクラスとほとんど同じです。)

必要に応じて使用できます。ここで、 queryable は 1 つのテンプレート パラメーターを受け取ります。

queryable< vector<int> > q;

これはquerable、パラメータが 1 つだけのテンプレートであることを意味します。

template <typename T>
struct queryable;

次に、複数のパラメーターを持つ特殊化を使用します。

template <typename ValueType, template<class T,class = allocator<T> > class ContainerTemplate>
struct queryable< ContainerTemplate<Contained> > {
        typedef ValueType value_type;
        typedef ContainerTemplate<ValueType> container_type;
        typedef typename container_type :: allocator_type A;
        // typedef ContainerTemplate <WhateverOtherValueTypeYouWish> ...
        // typedef A :: rebind <SomeOtherType> ...
};

コメントアウトされた最後の 2 行は、必要に応じて他の型を作成して、クラス テンプレートContainerTemplateとして使用する方法を示しています。またはまたはまたはそのようなものです。ContainerTemplatevectorlistset

@MikeSeymour が指摘したように、を使用すると、 allocatorクラス templaterebindにアクセスできる可能性があります。

ideone のサンプル コード

于 2012-01-18T13:20:39.597 に答える