私がそれを正しく理解していれば、a=std::move(b)
は参照aをbのアドレスにバインドします。そして、この操作の後、bが指す内容は保証されません。
_move_iterator
_ here の実装にはこの行があります
_auto operator[](difference_type n) const -> decltype(std::move(current[n]))
{ return std::move(current[n]); }
_
ただし、配列内の要素を_std::move
_することは意味がないと思います。 a=std::move(b[n])
の場合はどうなりますか?
次の例も私を混乱させます:
_std::string concat = std::accumulate(
std::move_iterator<iter_t>(source.begin()),
std::move_iterator<iter_t>(source.end()),
std::string("1234"));
_
concat
自体が結果を格納するためにメモリの連続チャンクを割り当てるため、source
と重複することはありません。 source
のデータはconcat
にコピーされますが、移動されません。
私がそれを正しく理解していれば、
a=std::move(b)
は参照a
をb
のアドレスにバインドします。そして、この操作の後、bが指す内容は保証されません。
ああ、いいえ:a
は必然的に参照ではありません。上記の_std::move
_の使用により、コンパイラーはdecltype(a)::operator=(decltype(b)&&)
が存在する場合、それを呼び出すことができます。このような代入演算子は、a
への代入中にb
を保存する必要はありませんが、b
は、破壊のためにsome正常な状態のままにしておく必要があります。
ただし、配列内の要素を_
std::move
_することは意味がないと思います。a=std::move(b[n])
の場合はどうなりますか?
それは理にかなっています...これは、各配列要素を別の変数に効率的に割り当て/移動できることを意味しますが、要素ごとに1回だけです。それらが移動元になった後、適切に記述された移動コンストラクターまたは代入演算子は、オブジェクトを有効であるが指定されていない状態のままにする必要があります。つまり、通常、オブジェクトを読み取る前に、オブジェクトを再度設定する必要があります。
私の ここに答える は、誰かがlist
からvector
に要素を追加/移動する方法を示しています。現在のC++標準では、そのように直接move_iteratorsを作成できます。
以下のコードは、ソースイテレータ範囲の要素から移動する場合に、-古いコンパイラ/ C++標準でも-_make_move_iterator
_を_std::copy
_で使用する方法を示しています。
_#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
struct X
{
X(int n) : n_(n) { }
X(const X& rhs) : n_(rhs.n_) { }
X(X&& rhs) : n_{ rhs.n_ } { rhs.n_ *= -1; std::cout << "=(X&&) "; }
X& operator=(X&& rhs) { n_ = rhs.n_; rhs.n_ *= -1; std::cout << "=(X&&) "; return *this; }
int n_;
};
int main()
{
std::vector<X> v{2, 1, 8, 3, 4, 5, 6};
std::vector<X> v2{};
std::copy(v.begin() + 2, v.end(), std::insert_iterator(v2, v2.end()));
for (auto& x : v)
std::cout << x.n_ << ' ';
std::cout << '\n';
std::copy(std::make_move_iterator(v.begin() + 2), std::make_move_iterator(v.end()), std::insert_iterator(v2, v2.end()));
for (auto& x : v)
std::cout << x.n_ << ' ';
std::cout << '\n';
}
_
出力:
_2 1 8 3 4 5 6
=(X&&) =(X&&) =(X&&) =(X&&) =(X&&) 2 1 -8 -3 -4 -5 -6
_
コードは colir で実行/編集できます。
_move_iterator
_の目的は、入力の右辺値をアルゴリズムに提供することです。
あなたの例auto a=std::move(b[n])
は配列内の値を移動しませんが、配列から値を移動します。これは賢明なことです。
_std::accumulate
_の秘訣は、 std :: stringのoperator + の定義です(accumulateのデフォルトバージョンは_operator+
_を使用することに注意してください。右辺値引数に対して特別な最適化があります。この場合、accumulate
は式_init + *begin
_を使用するため、オーバーロード番号7が重要です。これにより、右側の引数のメモリが再利用されます。これが実際に最適化であることが判明した場合は、はっきりしていません。
http://en.cppreference.com/w/cpp/iterator/move_iterator はこれを言います:
std :: move_iteratorは、間接参照によって基になるイテレータによって返された値が右辺値に変換されることを除いて、基になるイテレータ(少なくともInputIteratorである必要があります)とまったく同じように動作するイテレータアダプタです。
範囲を受け入れ、範囲の最初から最後までイテレーターをウォークし、逆参照されたイテレーターで操作を実行するほとんどの(すべてではないにしても)標準アルゴリズム。たとえば、_std::accumulate
_は次のように実装できます。
_template <class InputIterator, class T>
T accumulate (InputIterator first, InputIterator last, T init)
{
while (first!=last) {
init = init + *first;
++first;
}
return init;
}
_
first
とlast
が通常のイテレータである場合(呼び出しは
_std::accumulate(source.begin(), source.end(), std::string("1234"));
_
の場合、_*first
_は文字列への左辺値参照であり、式_init + *first
_はstd::operator+(std::string const&, std::string const&)
を呼び出します(オーバーロード1 ここ )。
ただし、通話が
_std::accumulate(std::make_move_iterator(source.begin()), std::make_move_iterator(source.end()), std::string("1234"));
_
次に、std :: Accumulate内で、first
とlast
は移動イテレータであるため、_*first
_は右辺値の参照です。これは、_init + *first
_が代わりにstd::operator+(std::string const&, std::string &&)
を呼び出すことを意味します(オーバーロード7)。