0

ムーブ セマンティクスの「基本的なアイデア」は理解していると思いますが、独自のマップを実装する段階になったときに、ユース ケースを作成してムーブを説明しようとしたときに立ち止まって考え始めました。マップの中心。私が間違っている場合は修正してください。しかし、移動セマンティクスのビジネス全体がどのように機能するかを理解しているのは、不要なコピーを回避するのに役立つと思われることですか? 右?ここで、たとえばマップを取り上げます。この例の目的のために、マップが次のようにモデル化されていると仮定します。

class Map
{
Link* impl_;//THIS IS A POINTER TO A LINK WHICH HAS A parent, left and right (of Link type)
Map(Map&& tmp);//move ctor
//unnecessary code ommited
};

そして、ここに問題があります:マップの移動ctorを考えようとすると、作成する必要があるすべてのリンクに新しいスペースを割り当て、それらのポインターをtmpマップのポインターと交換することを回避する方法がわかりませんオブジェクト (移動 ctor に引数として渡されます)。
とにかく、スペースを割り当てる必要がありますか?

4

4 に答える 4

3

合計 5 つの操作が必要です: 従来の "ビッグ 3" (コピー コンストラクター、コピー代入演算子、デストラクタ) と 2 つの新しい移動操作 (移動コンストラクター、移動代入演算子):

// destructor
~Map();

// copy constructor
Map(const Map& that);

// move constructor
Map(Map&& that)
{
    impl_ = that.impl_;
    that.impl_ = 0;
}

// copy assignment operator
Map& operator=(const Map& that);

// move assignment operator
Map& operator=(Map&& that)
{
    using std::swap;
    swap(impl_, that.impl_);
    return *this;
}

移動割り当て演算子の背後にある基本的な考え方は、スワップを実行した後に再度検査しない場合swap(map1, map2)と同じ観察可能な副作用があるということです。右辺値は prvalue または xvalue のいずれかであることを思い出してください。定義上、 prvalue を評価すると常に新しいオブジェクトが作成されるため、クライアントはprvalue で指定されたオブジェクトを 2 回検査することはできません。このトリックを確認する唯一の方法は、 などの xvalue から移動することですが、変更される可能性があることは明らかです。map1 = map2map2std::move(map_variable)map_variable

コピー時にも例外セーフな代入が必要な場合は、コピー代入演算子 ( を取る) と移動代入演算子 ( を取る)の両方を一般化された代入演算子 ( を取る ) に組み合わせることができます。次に、必要な操作は合計 4 つだけです。const Map&Map&&Map

// exception safe copy/move assignment operator
Map& operator=(Map that)
{
    using std::swap;
    swap(impl_, that.impl_);
    return *this;
}

代入演算子のこのバリアントは、引数を値で受け取ることに注意してください。引数が左辺値の場合、コピー コンストラクターが初期化thatし、引数が右辺値の場合、移動コンストラクターがジョブを実行します。std::swap(また、すでに移動操作を提供している場合、特殊化によってさらに大幅なパフォーマンスが向上する可能性は低いことに注意してください。)

于 2010-10-27T15:32:34.103 に答える
3

リンク ポインターを再割り当てするだけで済みます。他のすべてのリンク ポインターはそれにアタッチされているため、新しいマップの一部になります。

Map(Map&& tmp) :impl_(tmp.impl_) { tmp.impl_ = nullptr; }

これは、他のデータ メンバーがないことを前提としています。

于 2010-10-27T14:59:01.447 に答える
2

既存のコンテナーを再発明しないという標準的な免責事項は別として、割り当てを行わずにルート ノード ポインターを割り当てるだけで十分ではないでしょうか?

于 2010-10-27T14:59:20.027 に答える
0

古い C++ (0x ではない) で移動セマンティクスを実装することは可能ですが、明示的に行う必要があり、よりトリッキーです。

class X
{
// set access specifiers as required
   struct data
   {
      // all the members go here, just plain easy-to-copy members
   } m;

   data move()
   {
      data copy(m);
      m.reset(); // sets everything back to null state
      return m;
   }

   explicit X( const data& d ) : m(d)
   {
   }
  // other members including constructors
};

X::data func() // creates and returns an X
{
  X x; // construct whatever with what you want in it
  return x.move();
}

int main()
{
   X x(func());
   // do stuff with x
}

また、X は上記でコピー不可および割り当て不可にすることができ、データはヒープ上に作成されたアイテムを持つことができ、クリーンアップを担当するのは X のデストラクタです。データが move() 関数によってリセットされると、所有権が譲渡されているため、parting X はクリーンアップするものがなくなります。

一般に、データ構造体は X 内でパブリックにする必要がありますが、そのすべてのメンバーは X を友人としてプライベートにする必要があります。したがって、ユーザーはそこに直接アクセスすることはありません。

別の X にアタッチせずに X で move() を呼び出すと、「リーク」する可能性が高いことに注意してください。したがって、上の func() だけを呼び出すと、リークします。X from data のコンストラクタがスローする場合にも注意する必要があります (そのデストラクタは呼び出されないため、クリーンアップは自動的に行われません)。データ自体のコピーコンストラクターがスローした場合、さらに問題が発生します。通常、データには重いオブジェクトではなく軽いもの (ポインタと数値) が含まれているため、これらのいずれも発生しません。

于 2010-10-27T15:27:47.960 に答える