Что такое итератор перемещения для
если я правильно понял,a=std::move(b)
привязывает ссылку a к адресу b. И после этой операции содержимое, на которое указывает b, не гарантируется.
реализация move_iterator
здесь эта строка
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
но не переехал.
3 ответов
если я правильно понял,
a=std::move(b)
персонализация ссылкаa
в адресb
. И после этой операции содержимое, на которое указывает b, не гарантируется.
Ах, нет: a
- это не обязательно ссылка. Выше использование std::move
также предоставляет компилятору разрешение на вызов decltype(a)::operator=(decltype(b)&&)
если он существует: такие операторы присваивания используются при назначении a
значение b
не нужно сохранять, но!--4--> должен быть оставлен в некоторые вменяемое состояние для уничтожения.
однако, я не думаю, что это имеет смысл
std::move
элемент в массиве. Что произойдет, еслиa=std::move(b[n])
?
это может иметь смысл... это просто означает, что каждый элемент массива может быть эффективно назначен/перемещен в другую переменную, но только один раз на элемент. После того, как они были перемещены, правильно написанный конструктор перемещения или оператор присваивания должны уйти объекты в допустимом, но неопределенном состоянии, что означает, что вы обычно хотите установить их снова перед чтением из них.
мой ответ тут показывает, как кто-то может добавлять/перемещать элементы из 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
код может быть запущен / ред. от coliru.
цель move_iterator
является предоставление алгоритмов с rvalues их входов.
ваш пример auto a=std::move(b[n])
не перемещает значение в массиве, а перемещает его из него,что является разумной вещью.
фишка в std::accumulate
определение оператор+ для std:: string (помните, что версия accumulate по умолчанию использует operator+
. Он имеет специальную оптимизацию для Аргументов rvalue. Для нашего случая перегрузка № 7 является важной с accumulate
использует выражение init + *begin
. Это попытается повторно использовать память аргумента правой стороны. Если это на самом деле оказывается оптимизацией, это не совсем понятно.
http://en.cppreference.com/w/cpp/iterator/move_iterator говорит:
std:: move_iterator-это адаптер итератора, который ведет себя точно так же, как базовый итератор (который должен быть, по крайней мере, InputIterator), за исключением того, что разыменование преобразует значение, возвращаемое базовым итератором, в rvalue.
большинство (если не все) стандартных алгоритмов, которые принимают диапазон, проходят итератор от начала диапазона до конца, и выполнить операцию на разыменование итератора. Например, 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
является ссылкой lvalue на строку и выражение 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:: аккумулировать,first
и last
есть двигаться итераторы и поэтому *first
- это rvalue ссылку. Это значит, что init + *first
звонки std::operator+(std::string const&, std::string &&)
вместо (перегрузка 7).