titulo tag etiqueta c++ c++11 iterator move

c++ - tag - Para qué es move_iterator



etiqueta de titulo en html (3)

Si lo comprendo correctamente, a=std::move(b) enlaza la referencia a la dirección de b . Y después de esta operación, el contenido que apunta no está garantizado.

Ah, no: a no es necesariamente una referencia. El uso anterior de std::move también otorga permiso al compilador para llamar a decltype(a)::operator=(decltype(b)&&) si existe: tales operadores de asignación se usan cuando durante la asignación a a el valor de b necesario no debe conservarse, pero aún debe dejarse en algún estado sano para su destrucción.

Sin embargo, no creo que tenga sentido std::move un elemento en una matriz. ¿Qué sucede si a=std::move(b[n]) ?

Puede tener sentido ... solo significa que cada elemento de la matriz se puede asignar / mover de manera eficiente a otra variable, pero solo una vez por elemento. Después de que se hayan movido de, un constructor de movimientos u operador de asignación correctamente escrito debe dejar los objetos en un estado válido pero no especificado, lo que significa que normalmente querrá configurarlos nuevamente antes de leerlos.

Mi respuesta aquí muestra cómo alguien podría agregar / mover elementos de una list a un vector . Con los estándares actuales de C ++, puedes crear move_iterators directamente así.

El siguiente código muestra cómo, incluso con compiladores más antiguos / estándares de C ++, make_move_iterator se puede usar con std::copy si desea moverse desde los elementos en el rango del iterador de origen.

#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''; }

Salida:

2 1 8 3 4 5 6 =(X&&) =(X&&) =(X&&) =(X&&) =(X&&) 2 1 -8 -3 -4 -5 -6

El código se puede ejecutar / editar en coliru .

Si lo comprendo correctamente, a=std::move(b) enlaza la referencia a la dirección de b. Y después de esta operación, el contenido que apunta no está garantizado.

La implementación de move_iterator here tiene esta línea.

auto operator[](difference_type n) const -> decltype(std::move(current[n])) { return std::move(current[n]); }

Sin embargo, no creo que tenga sentido std::move un elemento en una matriz. ¿Qué sucede si a=std::move(b[n]) ?

El siguiente ejemplo también me confunde:

std::string concat = std::accumulate( std::move_iterator<iter_t>(source.begin()), std::move_iterator<iter_t>(source.end()), std::string("1234"));

Dado que el concat asignará una porción continua de memoria para almacenar el resultado, que no tendrá ninguna superposición con la source . Los datos en la source se copiarán a concat pero no se concat .


El propósito de move_iterator es proporcionar algoritmos con valores de sus entradas.

Su ejemplo auto a=std::move(b[n]) no mueve un valor en una matriz, sino que lo saca de ella, lo cual es una cosa sensata.

El truco en std::accumulate es la definición de operador + para cadena estándar (recuerde que la versión predeterminada de acumulación utiliza operator+ . Tiene una optimización especial para los argumentos de valor. Para nuestro caso, la sobrecarga número 7 es la más importante desde que se accumulate usa la expresión init + *begin . Esto intentará reutilizar la memoria del argumento del lado derecho. Si esto realmente resulta ser una optimización, no está realmente claro.


http://en.cppreference.com/w/cpp/iterator/move_iterator dice esto:

std :: move_iterator es un adaptador de iterador que se comporta exactamente igual que el iterador subyacente (que debe ser al menos un InputIterator), excepto que la desreferenciación convierte el valor devuelto por el iterador subyacente en un valor r.

La mayoría (si no todos) de los algoritmos estándar que aceptan un rango, recorren un iterador desde el principio hasta el final y realizan una operación en el iterador sin referencia. Por ejemplo, std::accumulate puede implementarse como:

template <class InputIterator, class T> T accumulate (InputIterator first, InputIterator last, T init) { while (first!=last) { init = init + *first; ++first; } return init; }

Si el first y el last son iteradores normales (la llamada fue

std::accumulate(source.begin(), source.end(), std::string("1234"));

, luego *first es una referencia lvalue a cadena, y la expresión init + *first llamará std::operator+(std::string const&, std::string const&) (sobrecarga 1 aquí ).

Sin embargo, si la llamada era

std::accumulate(std::make_move_iterator(source.begin()), std::make_move_iterator(source.end()), std::string("1234"));

luego dentro de std :: acumule, el first y el last son los iteradores de movimiento y, por lo tanto, *first es una referencia de valor. Esto significa que init + *first llama a std::operator+(std::string const&, std::string &&) lugar (sobrecarga 7).