2012-03-18 7 views
5

ユーザが引数として渡したアロケータがメモリ自体を取得するために使用されたときに、ライブラリ内のstd::uninitialized_fill()でメモリを初期化するのは意味がありますか?アロケータがallocate()メソッド以外の独自のconstruct()メソッドを提供するはずであるため、これは尋ねます。その実装は標準のものとは異なる可能性がありますので、おそらくstd::uninitialized_fill()がすべての場合に適切であるとは限りません。すべてのアロケータでstd :: uninitialized_fill()を使用するのは意味がありますか?

正確には、Stroustrup(付録E「標準ライブラリ例外安全性」、セクションE. 3.1)によって書かれたC++の本からの疑問は、template<class T, class A> vector<T,A>::vector(size_type n, const T& val, const A& a):allocator aベクトルのメモリを取得するために使用された場合、得られたメモリを初期化するためにstd::uninitialized_fill()が使用されます。

std::uninitialized_fill()の実装では、内部的にメモリの初期化に標準の配置を使用していますが、ベクトルコンストラクタの引数として渡されるアロケータのメソッドは存在しません。

+1

'stdの何:: initialize_fill() '? 'std :: uninitialized_fill()'を意味しますか? 'initialize_fill()'関数は、 'uninitialized_fill()'とは異なり、C++標準のどこにも現れません。 –

+0

@Insilico申し訳ありませんが、それはタイプミスでした。ありがとうございました。私はその問題を解決した。 – Martin

+2

これは私の質問に関連しているようです:http://stackoverflow.com/questions/9727556/is-uninitialized-copy-fillin-first-in-last-for-dest-aa-an-oversight-inth実際には普通の 'uninitialized_fill'を使うのが安全だろうと考えています –

答えて

2

私はベクトルのGCCの実装を確認し、それが読み:アロケータの情報が消えないようなので、

vector(size_type __n, const value_type& __value, 
     const allocator_type& __a = allocator_type()) 
    : _Base(__n, __a) 
    { _M_fill_initialize(__n, __value); } 

    ... 

    _M_fill_initialize(size_type __n, const value_type& __value) 
    { 
    this->_M_impl._M_finish = 
     std::__uninitialized_fill_n_a(this->_M_impl._M_start, __n, __value, 
            _M_get_Tp_allocator()); 
    } 

を、それが見えます。しかし、uninitialized_fill_n_aのコードは2つのオーバーロード(下を参照)を持っています.1つは汎用アロケータ用、もう1つはstd::allocatorです。

constructstd::allocatorの特殊コールは、単にstd::uninitialized_fill()を呼び出します。

だから、私の結論は、一般的なアロケータのために仮定することはできません以下、

1)すべてのstd::allocatorconstruct sが新しい配置と

2と同じ効果を持っている)、または少なくともGCCですstd::vectorはそれを想定していません。 (@Michael Burrによれば、C++ 03でこれを仮定できますが、残念ながらC++ 11は言及していません)。

したがって、使用構成(std::allocator_traits<Alloc>::construct(alloc, pointer, value))です。

私の個人的な意見は、(これを調査した後)、

I)std::uninitialized_fillはそれはアロケータとの最後のオプションの引数(または他の過負荷)、

II)などを取るべきで、不良品であるということです回避策として、実装の詳細の中に例外を処理することを含む.uninitialized_fill(first, last, value, alloc)関数を持たなければなりません(以下の例では、失敗時に構造がどのように破棄されるかを示しています)。

あなたはアロケータについての情報を持っている(と基本的にあなたがそれを再実装する必要がある場合ⅲ)現在std::unitialized_fillはかなり無駄です)


今の上で参照コード:

template<typename _ForwardIterator, typename _Size, typename _Tp, 
      typename _Allocator> 
    _ForwardIterator 
    __uninitialized_fill_n_a(_ForwardIterator __first, _Size __n, 
          const _Tp& __x, _Allocator& __alloc) 
    { 
     _ForwardIterator __cur = __first; 
     __try 
     { 
      typedef __gnu_cxx::__alloc_traits<_Allocator> __traits; 
      for (; __n > 0; --__n, ++__cur) 
      __traits::construct(__alloc, std::__addressof(*__cur), __x); 
      return __cur; 
     } 
     __catch(...) 
     { 
      std::_Destroy(__first, __cur, __alloc); 
      __throw_exception_again; 
     } 
    } 

    template<typename _ForwardIterator, typename _Size, typename _Tp, 
      typename _Tp2> 
    inline _ForwardIterator 
    __uninitialized_fill_n_a(_ForwardIterator __first, _Size __n, 
          const _Tp& __x, allocator<_Tp2>&) 
    { return std::uninitialized_fill_n(__first, __n, __x); } 
0

デフォルト以外のアロケータのconstruct()関数の実装は、配置の新機能(表32 - C++ 03のアロケータ要件に従って)を必要とします。

std::uninitialized_fill()(これは、範囲内の各要素に対して新しい配置を実行すると定義されます)は、カスタムアロケータが使用されていても有効です。

+2

私は、 'constructin 'の呼び出しを' uninitialized_fill'と置き換えても構わないと尋ねています。 'construct 'は、要件を超えた追加の効果を持つかもしれません。 – Potatoswatter

1

私はむしろconstructと呼んでいます。

カスタムアロケータを提供するさまざまな理由があり、より優れた割り当てポリシー(プール/スタックストレージを使用)だけではありません。

カスタムコンテナでの不正なアロケータの使用状況を追跡するのに役立つデバッギングアロケータも見てきました。また、メモリの使用状況を追跡したり、統計情報を収集するために、アロケータを使用することもできます。constructは、オブジェクトの構築以外の追加の効果を持つ可能性があります(またはそうでない)ため、必ずしもa簡単な配置new。そのアドレスにconstructと呼ばせずにdestroyを呼び出して、私のデバッグアロケータで

は、前のエラーだった(と同様にdestroy呼ばせずにメモリを再利用)、非常に少なくとも私はdestroyと配置と一致してペアリングconstructことをお勧めしますので、 newに明示的なデストラクタ呼び出しを行います。

+0

'destroy'ではなく、' destruct'(フランス語とは違って、これは数回噛まれました。非対称です:)。 –

+0

@AlexandreC .:ありがとう、それは私がチェックしないために得るものです... –

関連する問題