この投稿は少しとりとめのないものなので、本題に入る前に、私が尋ねていることを明確にしたいと思います: コードに move 対応のセッターを追加しましたか? 努力する価値があると思いましたか? そして、私が見つけた動作のどの程度がコンパイラ固有のものであると期待できますか?
ここで注目しているのは、複雑な型のプロパティを設定する場合に、move 対応のセッター関数を追加する価値があるかどうかです。ここでは、移動が有効Bar
になっFoo
ていて、Bar
設定できるプロパティがあります。
class Bar {
public:
Bar() : _array(1000) {}
Bar(Bar const & other) : _array(other._array) {}
Bar(Bar && other) : _array(std::move(other._array)) {}
Bar & operator=(Bar const & other) {
_array = other._array;
return *this;
}
Bar & operator=(Bar && other) {
_array = std::move(other._array);
return *this;
}
private:
vector<string> _array;
};
class Foo {
public:
void SetBarByCopy(Bar value) {
_bar = value;
}
void SetBarByMovedCopy(Bar value) {
_bar = std::move(value);
}
void SetBarByConstRef(Bar const & value) {
_bar = value;
}
void SetBarByMove(Bar && value) {
_bar = std::move(value);
}
private:
Bar _bar;
};
一般的に言えば、過去に非組み込み型のセッター関数に const-ref を使用していました。私が調べたオプションは、値で渡してから move ( SetByMovedCopy
)、const-ref で渡してから copy ( SetByConstRef
)、最後に r-value-ref で受け入れてから move ( SetByMove
) でした。ベースラインとして、値渡しとコピー ( SetByCopy
) も含めました。FWIW、値渡しと右辺値参照のオーバーロードの両方を含めると、コンパイラはあいまいさを訴えました。
VS2010 コンパイラでの実験で、これは私が見つけたものです:
Foo foo;
Bar bar_one;
foo.SetByCopy(bar_one);
// Bar::copy ctor called (to construct "value" from bar_one)
// Foo::SetByCopy entered
// Bar::copy operator= called (to copy "value" to _bar)
// Foo::SetByCopy exiting
// Bar::dtor called (on "value")
value
からコピー構築されbar_one
、次にvalue
にコピーされbar
ます。value
破壊され、完全なオブジェクトを破壊するコストが発生します。2 回のコピー操作が実行されます。
foo.SetByMovedCopy(bar_one);
// Bar::copy ctor called (to construct "value" from bar_one)
// Foo::SetByCopy entered
// Bar::move operator= called (to move "value" into _bar)
// Foo::SetByCopy exiting
// Bar::dtor called (to destruct the moved "value")
value
は からコピー構築されbar_one
、次にvalue
に移動されます。その後、おそらくより低いコストで、関数の終了後に_bar
ガットが破棄されます。value
1 回のコピー操作と 1 回の移動操作。
foo.SetByConstRef(bar_one);
// Foo::SetByConstRef entered
// Bar::copy operator= called (to copy bar_one into _bar)
// Foo::SetByConstRef exiting
bar_one
に直接コピーされ_bar
ます。1回のコピー操作。
foo.SetByMove(std::move(bar_one))
// Foo::SetByMove entered
// Bar::move operator= called (to move "value" into _bar)
// Foo::SetByMove exited
bar_one
に直接移動され_bar
ます。1回の移動操作。
そのため、この場合は const-ref と move バージョンが最も効率的です。さて、もっと重要なことに、私がやろうとしていることは次のようなものです:
void SetBar(Bar const & value) { _bar = value; }
void SetBar(Bar && value) { _bar = std::move(value); }
ここでわかったことは、 を呼び出すFoo::SetBar
と、左辺値または右辺値のどちらを渡すかに基づいて、コンパイラが関数を選択するということです。std::move
次のように呼び出すことで、問題を強制できます。
foo.SetBar(bar_one); // Const-ref version called
foo.SetBar(Bar()); // Move version called
foo.SetBar(std::move(bar_one)); // Move version called
これらすべてのムーブ セッターを追加することを考えると身震いしますが、SetBar
関数にテンポラリが渡された場合にパフォーマンスが大幅に向上する可能性があり、適切な場所に適用することでさらに向上できると思いstd::move
ます。