4

私は物事がどのように機能するかを知りたいので、c++標準ライブラリを深く掘り下げてきました。先日、何かが起こった。

包含者(例:)はstd::vector<int, std::allocator<int> >、割り当てに指定されたアロケータを使用する必要があります。具体的には、標準は次のように述べています。

23.1.8

この句で定義されているすべてのコンテナタイプのコピーコンストラクタは、それぞれの最初のパラメータからアロケータ引数をコピーします。これらのコンテナタイプの他のすべてのコンストラクタは、値型がコンテナの値型と同じアロケータであるAllocator&引数(20.1.5)を取ります。この引数のコピーは、各コンテナーオブジェクトの存続期間中に、これらのコンストラクターおよびすべてのメンバー関数によって実行されるメモリ割り当てに使用されます。この句で定義されているすべてのコンテナタイプで、メンバーget_allocator()は、コンテナの構築に使用されるAllocatorオブジェクトのコピーを返します。

また、標準の後半では、次のようになっています(いくつかの異なる場所で、1つを選択します)。

explicit deque(size_type n, const T& value = T(), const Allocator& = Allocator());

効果:指定されたアロケータを使用して、値のコピーがn個ある両端キューを作成します。

OK、それでは私の質問に移ります。

std::vector例として、次のようなものを実装するための自然で効率的な方法を見てみましょう。

vector<T, A>::vector(const vector& x)

次のようになります。

template <class T, class A>
vector<T, A>::vector(const vector& x)  {
    pointer p = alloc_.allocate(x.size());
    std::uninitialized_copy(x.begin(), x.end(), p);
    first_ = p;
    last_  = p + x.size();
    end_   = p + x.size();
}

具体的には、メモリを割り当ててから、すべてのメンバーをコピーして配置します。new value_type[x.size()]デフォルトでは、配列を上書きするためだけに配列を作成するため、このようなことをわざわざ行う必要はありません。

しかし、これはコピー構築を行うためにアロケータを使用しません...

次のようなループを手動で作成できます。

while(first != last) {
    alloc_.construct(&*dest++, *first++);
}

しかし、それは無駄であり、ほぼ同じですstd::uninitialized_copy。唯一の違いは、新しい配置の代わりにアロケータを使用することです。

したがって、標準に次のような(私には明らかなように見える)一連の関数がないことは見落としだと思いますか?

template <class In, class For, class A>
For uninitialized_copy(In first, In last, For dest, A &a);

template <class In, class Size, class For, class A>
For uninitialized_copy_n(In first, Size count, For dest, A &a);

template <class For, class T, class A>
void uninitialized_fill(For first, For last, const T& x, A &a);

template <class For, class Size, class T, class A>
void uninitialized_fill_n(For first, Size count, const T& x, A &a);

これらのタイプの関数は(手動で実装するのは簡単ですが...例外安全にしようとするまで)、独自のコンテナーなどを実装してコピー構造を効率的に使用したい場合は、かなり役立つと思います。アロケータを使用している間。

考え?

4

3 に答える 3

3

それ自体を「見落とし」と呼べるかどうかはわかりません。

いいえ、これらの特殊なアルゴリズムに独自のアロケータを提供することはできません。しかし、標準に含まれていないものもあります。

@MarkBは、標準がこれを実行すべきではないという非常に正当な理由を識別します(範囲には、コンテナーのアロケーターに関する知識がありません)。私はそれが単なる固有の制限であるとまで言っていました。

uninitialized_copyアロケータがどうあるべきかを知っていれば、いつでも自分のニーズに合わせて再発明することができます。これは2行のforループです。

于 2012-03-15T20:45:59.263 に答える
1

これらの関数がフリー関数である場合、アロケータタイプがイテレータによって保持されないため、コンパイラがアロケータの不一致を検出できる方法がわかりません。これにより、さまざまな見つけにくい問題が発生する可能性があります。

于 2012-03-15T20:34:42.100 に答える
0

はい、アロケータに関する情報が失われるため、(大きな)見落としだと思います。現在のプロトコルでは、アロケータはメモリ内にオブジェクトを正確に構築する方法を知っている唯一のアロケータです。

現在、Boostにはalloc_construct, alloc_destroy https://www.boost.org/doc/libs/1_72_0/libs/core/doc/html/core/alloc_construct.htmlが含まれています。 これは、少なくともの汎用バージョンの実装に少し役立ちますuninitialized_copy/fill/etc(Alloc a, ...)

于 2020-01-01T04:39:35.770 に答える