質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.77%

デフォルトアロケーターのconstructメンバ関数について

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,673

score 105

標題において、実装コードに疑問があります。

以下のコードのconstructメンバ関数で、placement newに渡すポインタをボイドポインターにキャストして渡しています。
なぜ、キャストしているのでしょうか。

また、new((void*)p) T(value)で、コピーコンストラクター?を呼んで、オブジェクトを構築していますが、なぜコピーコンストラクタを呼び出すのでしょうか。
デフォルトコンストラクターでは、不都合があるのでしょうか。

引用サイト: C++編(標準ライブラリ) 第28章 アロケータ

template <class T>
class allocator
{
public:
    // 型定義
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    typedef T* pointer;
    typedef const T* const_pointer;
    typedef T& reference;
    typedef const T& const_reference;
    typedef T value_type;

    // アロケータをU型にバインドする
    template <class U>
    struct rebind
    {
        typedef allocator<U> other;
    };

    // コンストラクタ
    allocator() throw(){}
    allocator(const allocater&) throw(){}
    template <class U> allocator(const allocator<U>&) throw(){}
    // デストラクタ
    ~allocator() throw(){}

    // メモリを割り当てる
    pointer allocate(size_type num, allocator<void>::const_pointer hint = 0)
    {
        return (pointer)( ::operator new( num * sizeof(T) ) );
    }
    // 割当て済みの領域を初期化する
    void construct(pointer p, const T& value)
    {
        new( (void*)p ) T(value);
    }

    // メモリを解放する
    void deallocate(pointer p, size_type num)
    {
        ::operator delete( (void*)p );
    }
    // 初期化済みの領域を削除する
    void destroy(pointer p)
    {
        p->~T();
    }

    // アドレスを返す
    pointer address(reference value) const { return &value; }
    const_pointer address(const_reference value) const { return &value; }

    // 割当てることができる最大の要素数を返す
    size_type max_size() const throw()
    {
        return numeric_limits<size_t>::max() / sizeof(T);
    }
};
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

なぜ、キャストしているのでしょうか。 

それは引用サイトの管理者に聞いてみないことには判りません。元々placement newの引数はvoid *なので、私ならいちいちキャストしたりはしませんが。引数がvoid *だから、合わせた方が良いかな、と考えたのでしょうか。

デフォルトコンストラクターでは、不都合があるのでしょうか。 

不都合というか、デフォルトコンストラクターで初期化した後で、改めてオブジェクトの値をコピーすることになり、二度手間になってしまいます。

コピーコンストラクタで実装していれば、

construct(ptr, T());
construct(ptr, T(other));
construct(ptr, T(value));
construct(ptr, T(param1, param2));


このように一つのメソッドでどのようなコンストラクタにも対応できますが、デフォルトコンストラクタ(引数なし)にすると、

construct(ptr);        // デフォルトコンストラクタならこれで済みますが

construct(ptr);
*ptr = T(other);    // 改めて代入する必要が生じます

construct(ptr);
*ptr = T(value);

construct(ptr);
*ptr = T(param1, param2);


このようにコード量が増えてしまいます。また、場合によってはパフォーマンスにも影響するかもしれません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/03/19 00:30 編集

    あっと、すみません。厳密には違います。

    vectorの`push_back`は
    ```C++
    void push_back(const T& x);
    void push_back(T&& x);
    ```
    このような宣言になっています。type()は一時オブジェクトが生成されて、下のメソッドが呼ばれます。そのxがallocatorのconstructメソッドに渡されることになります。

    キャンセル

  • 2016/03/19 00:33

    コピーコンストラクタを使用したnew(今回はplacement new)は、オブジェクトの構築にコピーコンストラクタを使用するということでしょうか。
    コピーコンストラクタを使用したnewを、初めて見た気がしたので、戸惑いました。

    キャンセル

  • 2016/03/19 00:51

    オブジェクトの複製を作ることはごく普通に行われていることなので、newでコピーコンストラクタを使うことは、さほど珍しいことではないと思います。
    placement newはそれ自体あまり使われることがないのですが、やはりコピーコンストラクタで初期化するのは一般的に行われていると思います。

    STLのコンテナで内部的によく使われるallocatorは、その使用状況としてオブジェクトのコピーが多いので、必然的にコピーコンストラクタによる構築になるのだと思います。

    キャンセル

+1

今さらですみません。

なぜ、キャストしているのでしょうか。

規格としてはキャストしないとまずいからです。
例えば、以下のようなオーバーロードをユーザが提供していた場合を考えてみてください。

void* operator new(size_t, int*);

この場合に、キャストせずに new(p) T(value) と書いてしまうと、標準ライブラリで提供されている ::operator new(size_t, void*) ではなく、ユーザが提供した ::operator new(size_t, int*) が呼ばれてしまいます。
それでは当初の目的が果たせないため、この場合の void* へのキャストは必須です。

デフォルトコンストラクターでは、不都合があるのでしょうか。

あります。
「デフォルト構築+コピー代入」の結果と「コピー構築」の結果が等しい保証は無いからです。
(場合によっては、コピー代入が不可な可能性すらあります)

いずれの質問についても、普通に使用している分には「そんなバカな」的な気がするかもしれませんが、標準ライブラリは使用される文脈が分からないため、対処可能な範囲でなるべくいつでも同じ挙動となるように注意深く実装されています。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.77%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る