0

std::vectorカスタマイズして使用するために、カスタムに継承しましたclass Vector。私の要件では、このpublic 継承は問題ありません。

ベクトル配列のコピーを複数回作成しないようにすることが1つの目的であるため、このカスタムクラスを「所有権」ベースにすることにしました。

Vector<A> vA1(100); // vA1 is allocated A[100]
Vector<A> vA2 = vA1; // vA2 refers to original A[100] and ...
                     // ... vA1 is now blank which is expected

これは、C++03での実装方法です。

template<typename T>
struct Vector : std::vector<T>
{
  // ... other constructors

  Vector (const Vector &copy) // copy constructor
  {
    this->swap(const_cast<Vector&>(copy)); // <---- line of interest
  }
  // 'operator =' is TBD (either same as above or unimplemented)
};

言語のルール/機能/慣習に違反していますか?このコードに何か悪いことはありますか?

編集:以下の回答の形で新しいアプローチを追加しました(これが実際のデモです)。

4

4 に答える 4

4

あなたが探しているのは移動セマンティクスだと思います。

Vector<A> vA1(100); // vA1 is allocated A[100]
Vector<A> vA2 = std::move(vA1); // vA2 refers to original A[100] and ...
                 // ... vA1 is now blank which is expected

コンパイラがムーブ セマンティクスをサポートしていない場合は、スマート ポインターを使用するか、 C++11 以前のコンパイラでムーブ セマンティクスをエミュレートできるboost::moveを参照することをお勧めします。

std::shared_ptr<Vector<A>> vA1(new Vector<A>(100)); // vA1 is allocated A[100]
std::shared_ptr<Vector<A>> vA2 = vA1; // vA2 refers to original A[100] and ...
vA1.reset();
                 // ... vA1 is now blank which is expected

またはさらに簡単:

Vector<A> vA1(100); // vA1 is allocated A[100]
Vector<A> vA2;
vA1.swap(vA2);// vA2 refers to original A[100] and ...
                 // ... vA1 is now blank which is expected
于 2012-09-30T17:06:18.840 に答える
3

C++11 の使用を実際に検討する必要があります。すべての標準コンテナーは移動可能であるため、多くの場合、つまり所有権を譲渡できることがわかっている場合は、セマンティクスに似ています。より正確には、関数からオブジェクトを返すとき、または一時オブジェクトを使用してオブジェクトを構築または割り当てるときに、コンテナーはコピーではなく移動されます。何らかの方法でこのメカニズムを再現しようとすると、ほぼ確実に予期しない動作が発生する場合があります。

C++11 では、移動可能なセマンティクスが言語に統合され、コンストラクターや代入を含む関数をオーバーロードして、オブジェクトが移動可能かどうかに応じて異なる動作をすることができます。

于 2012-09-30T17:08:52.047 に答える
2

あなたのコードは、const-correctness をサポートする規則を破っています。

const Vector<int> foo(12);
const Vector<int> bar(foo); // UB.
some_function_that_takes_Vector_by_value(bar); // UB.

「しかし、誰も のconstインスタンスを作成しVectorないので、UB は発生しません」と言うかもしれません。その場合、関数がパラメーターによって取得されたオブジェクトを変更してはならないという規則に従うために、少なくともコピー ctor が非 const 参照を取るようにすることができconst&ます。

それでも、個人的には、このようなトラップを作成するよりも、明示的にスワップして速度を「オン」にすることをユーザーに要求することを好みます。また、通常のコピーとは異なる動作をするものよりも、コピー ctor がまったくないことを好みます。実際にはコピーできないことを心に留めてVectorおかなければならない場合は、コピー構文を使用してコピーしないようにするべきではありません。

C++03 での不要なコピーの問題は新しいものではなく、IMO では新しい解決策は必要ありません。多くの C++ プログラマーは、省略を利用しswapてコピーする方法を既に知っています。不必要なコピーを回避する方法を知らない人は、特定のユーザーが要求する独自の方法で行うよりも、通常の方法で行うことを学習した方がよいでしょう。クラス。

于 2012-09-30T17:15:25.440 に答える
1

コメント/回答からの洞察により、現在のアプローチは物事に対処するのに良い考えではないことに気付きました。
メソッドを使用して「移動」操作をシミュレートしようとする変更された設計を考え出しました swap()(このコンテキストでは交換可能であると考えてください)。

より高速なコピー (つまり、スワッピングによる移動) を行いたいクラスまたはコンテナーの場合、以下のクラスを継承する必要があります (実際のファイルから直接コピーして貼り付けます)。

  /*
   * An empty CRTP base class for the classes which are allowing move sematics (effectively swap)
   * A given class `X` must implement `void swap(X&);` method for the movement (i.e. content swapping)
   * For example, `X` is deriving this class (i.e. `class X : public Movable<X> ...`)
   * ... below is an illustration for how to transfer contents of an `X` object to another
   * `X o1; ... X o2(o1.move()); // contents of o1 is now moved to o2`
   */
  template<typename Derived>
  class Movable
  {
    /*
     * Empty constructor for normal behavior
     */
    public: Movable ()
            {}

    /*
     * Copy constructor which does nothing; without this compiler errors out !
     */
    public: Movable (const Movable &copy)
            {}

    /*
     * Move constructor which will effectively call `Derived::swap()` method
     * After this, the contents of the object will be moved to each other
     * For syntactic sugar, it has been made `explicit`
     */
    public: explicit Movable (Movable &orig)
            {
              (*this)(orig);
            }

    /*
     * Moving while Not initializing object, one need to use `operator ()` and not `operator =`
     * If `operator =` is used then simply `move()` part will not happen; i.e. below 2 are same:
     * `obj1 = obj2.move();` and `obj1 = obj2;`
     */
    public: void operator () (Movable &orig)
            {
              static_cast<Derived*>(this)->swap(static_cast<Derived&>(orig));
            }
    /*
     * This method is called from `Derived` class when move sematics is intended
     */
    public: Movable& move ()
            {
              return *this;
            }
  };

ここでは、展開する方法を示します。

template<typename T>
struct Vector : std::vector<T>,
                Movable<Vector<T> >  // inherit as CRTP base
{
  // ... other methods
  typedef Movable<Vector<T> > Movable_;
  Vector (Movable_ &orig) : Movable_(orig) {}  // Declare a constructor
};

使用方法は次のとおりです。

Vector<A> vA1(100); // vA1 is allocated A[100]
Vector<A> vA2 = vA1; // normal copying (no change)
vA2 = vA1; // normal assignment (no change)
Vector<A> vA3(vA1.move()); // <------- "moves" contents from 'vA1' to 'vA3'
vA1(vA2.move()); // <------- "moves" contents from 'vA2' to 'vA1'
vA2 = vA3.move(); // normal copying from 'vA3' to 'vA2' ('vA3' unaffected)
于 2012-10-01T11:31:48.457 に答える