web-dev-qa-db-ja.com

テンプレートテンプレートパラメータがあるのに、allocator :: rebindが必要なのはなぜですか?

すべてのアロケータクラスには、次のようなインターフェイスが必要です。

template<class T>
class allocator
{
    ...
    template<class Other>
    struct rebind { typedef allocator<Other> other; };
};

そして、seアロケーターが次のような冗長な処理を行うクラス:

template<class T, class Alloc = std::allocator<T> >
class vector { ... };

しかし、なぜこれが必要なのですか?

言い換えれば、彼らはちょうど言ったことができませんでした:

template<class T>
class allocator { ... };

template<class T, template<class> class Alloc = std::allocator>
class vector { ... };

どちらがよりエレガントで冗長性が低く、(いくつかの同様の状況で)潜在的に安全ですか?
なぜ彼らはrebindルートをたどったのでしょうか。これにより、冗長性も高まります(つまり、Tを2回言わなければなりません)。

(同様の質問がchar_traitsと残りの部分に行きます...すべてにrebindがあるわけではありませんが、テンプレートテンプレートパラメーターの恩恵を受けることができます。)


編集:

ただし、複数のテンプレートパラメータが必要な場合、これは機能しません。

実際、それは非常にうまく機能します!

template<unsigned int PoolSize>
struct pool
{
    template<class T>
    struct allocator
    {
        T pool[PoolSize];

        ...
    };
};

vectorがこのようにのみ定義されている場合:

template<class T, template<class> class Alloc>
class vector { ... };

次に、あなたはただ言うことができます:

typedef vector<int, pool<1>::allocator> int_vector;

そして、それは完全にうまく機能しますなし(冗長に)intを2回言う必要があります。

また、rebind内のvector操作は、Alloc<Other>ではなくAlloc::template rebind<Other>::otherになります。

26
user541686

C++ 11のアルゴリズムの基礎、第1巻、第4章、p。 35:

template <typename T> 
struct allocator 
{  
   template <typename U>  
   using  rebind = allocator<U>; 
}; 

使用例:

allocator<int>::rebind<char> x;

C++プログラミング言語、第4版、セクション34.4.1、p。 998、デフォルトのアロケータクラスの「クラシック」再バインドメンバーにコメント:

template<typename U>
     struct rebind { using other = allocator<U>;};

BjarneStroustrupはこれを書いています:

好奇心旺盛な再バインドテンプレートは古風なエイリアスです。それはすべきだった:

template<typename U>
using other = allocator<U>;

ただし、アロケータは、そのようなエイリアスがC++でサポートされる前に定義されていました。

18
incises

しかし、なぜこれが必要なのですか?

アロケータクラスに複数のテンプレート引数がある場合はどうなりますか?

これは、インスタンス化サイトで少し冗長性がある場合でも、通常のテンプレート引数を使用することを優先して、テンプレートテンプレート引数を使用することが一般的に推奨されない理由の点ではほぼ同じです。多くの場合(ただし、おそらくアロケーターの場合はそうではありません)、その引数は必ずしもクラステンプレート(たとえば、テンプレートメンバー関数を持つ通常のクラス)であるとは限りません。

内部構文の一部が単純化されているという理由だけで、テンプレートテンプレートパラメータを使用すると(コンテナクラスの実装内で)便利な場合があります。ただし、ユーザーが使用したいアロケーターとして複数引数のクラステンプレートを持っていて、単一引数のクラステンプレートであるアロケーターを提供する必要がある場合は、事実上、ほぼのラッパーを作成する必要があります。彼がそのアロケータを使用しなければならない新しいコンテキスト。これは拡張性がないだけでなく、非常に不便になる可能性もあります。そして、現時点では、そのソリューションは、当初考えていた「エレガントで冗長性の低い」ソリューションとはほど遠いものです。 2つの引数を持つアロケータがあるとしましょう。ユーザーにとって最も簡単なのは、次のうちどれですか。

std::vector<T, my_allocator<T,Arg2> > v1;

std::vector<T, my_allocator_wrapper<Arg2>::template type > v2;

基本的に、実装の要求を満たすためだけに、ユーザーに多くの役に立たないもの(ラッパー、テンプレートエイリアスなど)を作成するように強制します。カスタムアロケータークラスの作成者にネストされた再バインドテンプレート(これは単なるテンプレートエイリアスです)を提供するように要求することは、代替アプローチで必要なすべてのゆがみよりもはるかに簡単です。

10
Mikael Persson

あなたのアプローチでは、アロケーターを単一のパラメーターを持つテンプレートにすることを強制していますが、常にそうであるとは限りません。多くの場合、アロケーターは非テンプレートである可能性があり、ネストされたrebindは同じタイプのアロケーターを返すことができます。その他の場合、アロケーターは追加のテンプレート引数を持つことができます。この2番目のケースは、std::allocator<>のケースであり、実装がデフォルト値を提供する限り、標準ライブラリ内のすべてのテンプレートに追加のテンプレート引数を含めることができます。また、rebindの存在はオプションである場合があり、allocator_traitsを使用してリバウンドタイプを取得できることにも注意してください。

標準では、ネストされたrebindは実際には単なるテンプレート化されたtypedefであると実際に言及されています。

§17.6.3.5/ 3注A:上記の表で再バインドされたメンバークラステンプレートは、事実上typedefテンプレートです。 [注:一般に、名前AllocatorがSomeAllocator<T>にバインドされている場合、Allocator::rebind<U>::otherSomeAllocator<U>と同じタイプです。ここで、someAllocator<T>::value_typeはT、SomeAllocator<U>::value_typeはUです。—終了注] AllocatorがSomeAllocator<T, Args>形式のクラステンプレートインスタンス化である場合、 Argsは0個以上の型引数であり、Allocatorは再バインドメンバーテンプレートを提供しません。標準のallocator_traitsテンプレートは、デフォルトでSomeAllocator<U, Args>の代わりにAllocator:: rebind<U>::otherを使用します。上記のフォームのテンプレートインスタンス化ではないアロケータタイプの場合、デフォルトは提供されません。

あらゆる種類のベクトルをとる関数を書きたいとしましょう。

そうすれば、書くことができる方がはるかに便利です

template <class T, class A>
void f (std::vector <T, A> vec) {
   // ...
}

書く必要があるよりも

template <class T, template <class> class A>
void f (std::vector <T, A> vec) {
   // ...
}

ほとんどの場合、そのような関数はとにかくアロケータを気にしません。

さらに、アロケータはテンプレートである必要はないことに注意してください。割り当てる必要のある特定のタイプに対して個別のクラスを作成できます。

アロケータを設計するさらに便利な方法は、おそらく

struct MyAllocator { 
   template <class T>
   class Core {
      // allocator code here
   };
};

その後、書くことが可能だったでしょう

std::vector <int, MyAllocator> vec;

やや誤解を招く表現ではなく

std::vector <int, MyAllocator<int> > vec;

上記のMyAllocatorrebindを追加した後、アロケーターとして使用できるかどうか、つまり、以下が有効なアロケータークラスであるかどうかはわかりません。

struct MyAllocator { 
   template <class T>
   class Core {
      // allocator code here
   };

   template <class T>
   struct rebind { using other=Core<T>; };
};
0
JohnB