4

Grid型でテンプレート化された単純な2Dコンテナである古いクラスを掘り起こしました。1つを作成するには、次のようにします。

Grid<SomeType> myGrid (QSize (width, height));

私はそれを「Qt-ish」にしようとしました...たとえば、それはの観点からサイズ操作をQSize行い、あなたはそれにインデックスを付けmyGrid[QPoint (x, y)]ます。ブールマスクを取り、マスクビットが設定された要素に対して操作を実行できます。あなたの要素があればそれがあなたのためにQColor生成することができるという専門分野もありQImageます。

しかし、私が採用した主要なQtイディオムの1つは、内部で暗黙的に共有することでした。これは、私が持っていたThinker-QtベースのプログラムのQColorベースのグリッドで非常に役立つことがわかりました。

しかし:-/私はたまたま次のようなものを書いた場合もありました:

Grid< auto_ptr<SomeType> > myAutoPtrGrid (QSize (width, height));

auto_ptr私がC++11に移行したときunique_ptr、コンパイラは当然のことながら文句を言いました。暗黙の共有には、必要に応じて同一のコピーを作成する機能が必要です...そして、auto_ptrコピーと所有権の譲渡を混同することで、このバグを一掃しました。コピー不可能なタイプと暗黙の共有は単純に混ざり合うことはなく、unique_ptr親切にも教えてくれます。

(注:のユースケースは、値ではなく参照によってグリッドを渡すため、実際には問題に気付かなかったことがありましたauto_ptr。それでも、これは悪いコードでした...そしてCのプロアクティブな性質++ 11は、発生する前に潜在的な問題を指摘しています。)

では、暗黙の共有のオンとオフを切り替えることができる汎用コンテナをどのように設計すればよいでしょうか。を使用していたときに、グリッド機能の多くが本当に必要でした。auto_ptrコピーできないタイプのコピーが無効になっていると、エラーが発生します。ただし、タイプがコピー可能である場合、デフォルトで暗黙的な共有が機能するのは便利です。

いくつかのアイデア:

  • 私はあなたの好みに応じて別々のタイプ(NonCopyableGridCopyableGrid)...または(UniqueGrid、 )を作ることができます...Grid
  • Gridコンストラクターにフラグを渡すことができます
  • 静的ファクトリメソッド(Grid::newNonCopyableGrid::newCopyable)を使用することもできますが、内部で関連するコンストラクターを呼び出すことになります...おそらくもっと説明的です
  • 可能であれば、含まれているタイプのコピー可能性を「検出」してから、実装でQSharedDataPointerを活用するかどうかに応じて、

これらの方法の1つを他の方法よりも選択する正当な理由はありますか、または人々はこの種の状況に完全に優れた方法を採用しましたか?

4

2 に答える 2

2

単一のコンテナーでそれを行う場合、最も簡単な方法はstd::is_copy_constructable、データ構造体がから継承されるかどうかを選択し、 移動セマンティクスをサポートしない)QSharedDataに置き換えることです。QSharedDataPointerstd::unique_ptr QScopedPointer

これは、QtとC ++ 11を一緒に使用できないため、私が考えていることの大まかな例にすぎません。

template<class T>
class Grid
{       
    struct EmptyStruct
    {
    };

    typedef typename std::conditional<
        std::is_copy_constructible<T>::value,  
        QSharedData,  
        EmptyStruct
    >::type GridDataBase;

    struct GridData : public GridDataBase
    {
        // data goes here
    };

    typedef typename std::conditional<
        std::is_copy_constructible<T>::value, 
        QSharedDataPointer<GridData>, 
        std::unique_ptr<GridData>
    >::type GridDataPointer;

public:
    Grid() : data_(new GridData) {}

private:
    GridDataPointer data_;
};
于 2012-06-19T06:25:39.817 に答える
1

免責事項

Gridテンプレートやユースケースがよくわかりません。しかし、私は一般的にコンテナを理解しています。したがって、この答えはあなたに当てはまるかもしれませんし、そうGrid<T>でないかもしれません。

Grid< unique_ptr<T> >一意の所有権とコピー不可を示す意図をすでに述べているのでT、コピーオンライトで同様のことを行うのはどうですか?

コピーオンライトをいつ使用するかを明示的に指定するのはどうでしょうか。

Grid< cow_ptr<T> >

Acow_ptr<T>は参照カウントのコピーを提供しますが、「非const dereference」ではT、refcountが1でない場合のコピーを実行します。したがってGrid、この程度のメモリ管理について心配する必要はありません。データバッファを処理するだけでよく、おそらくそのメンバーをGridのコピーおよび/または移動メンバー内で移動またはコピーする必要があります。

Acow_ptr<T>は、ラッピングすることでかなり簡単に石畳になりstd::shared_ptr<T>ます。これは、同様の問題を処理するときに約1か月前にまとめた部分的な実装です。

template <class T>
class cow_ptr
{
    std::shared_ptr<T> ptr_;
public:
    template <class ...Args,
              class = typename std::enable_if
                      <
                         std::is_constructible<std::shared_ptr<T>, Args...>::value
                      >::type
             >
        explicit cow_ptr(Args&& ...args)
            : ptr_(std::forward<Args>(args)...)
        {}

    explicit operator bool() const noexcept {return ptr_ != nullptr;}

    T const* read() const noexcept {return ptr_.get();}
    T      * write()
    {
        if (ptr_.use_count() > 1)
            ptr_.reset(ptr_->clone());
        return ptr_.get();
    }

    T const& operator*() const noexcept {return *read();}
    T const* operator->() const noexcept {return read();}

    void reset() {ptr_.reset();}
    template <class Y>
        void
        reset(Y* p)
        {
            ptr_.reset(p);
        }
};

書き込みが非常に少ないが読み取り/コピーが多い場合にCOWがより効果的になる傾向があるため、「書き込み」構文を非常に明示的にすることを選択しました。constアクセスを取得するには、他のポインターと同じように使用します。

p->inspect();  // compile time error if inspect() isn't const

writeただし、変更操作を行うには、メンバー関数を使用して呼び出す必要があります。

p.write()->modify();

shared_ptr非常に便利なコンストラクターがたくさんあり、それらすべてをで複製する必要はありませんでしたcow_ptr。したがって、cow_ptr表示されるコンストラクターの1つは、データメンバーに対しても機能する継承コンストラクターの貧弱な実装です。

関係演算子などの他のスマートポインタ機能を使用してこれを入力する必要がある場合があります。cow_ptrコピー方法を変更することもできますT。私は現在仮想関数を想定していますが、代わりにTのコピーコンストラクターを使用するようclone()に簡単に置き換えることができます。write

明示的なGrid< cow_ptr<T> >ものが本当にあなたのニーズに合わない場合、それはすべて良いことです。万が一に備えて共有したいと思いました。

于 2012-06-19T14:51:10.717 に答える