2010-01-26 16 views
17

En C++ 11, se puede obtener un impulso de la eficiencia mediante el uso de std::move cuando queremos mover (destructivamente copiar) los valores en un recipiente:C++ "paso de" contenedor

SomeExpensiveType x = /* ... */; 
vec.push_back(std::move(x)); 

pero no puedo encontrar algo yendo para el otro lado. Lo que quiero decir es algo como esto:

SomeExpensiveType x = vec.back(); // copy! 
vec.pop_back(); // argh 

Esto es más frecuente (la copia-pop) el adaptador de como stack. ¿Podría algo como esto existir:

Para evitar una copia? Y esto ya existe? No pude encontrar nada como eso en n3000.

Tengo la sensación de que me falta algo dolorosamente obvio (como lo innecesario), así que estoy preparado para "ru dum". : 3

+0

¿Qué implicaciones tiene el método de movimiento en las variables que están fuera del alcance? Por ejemplo, si creo un objeto, lo agrego a un contenedor miembro, entonces el objeto sale del alcance ... Como no se realizó ninguna copia, ¿el objeto en el contenedor miembro aún está definido? – Polaris878

+0

Sí, se movió al contenedor. Si no está seguro de cómo funcionan, querrá google rvalue references. – GManNickG

+0

gracias dulce ... tendré que profundizar en esto un poco más jajaja. A primera vista, parece que esto causaría problemas. – Polaris878

Respuesta

15

puedo estar equivocado total aquí, pero no es lo que desea simplemente

SomeExpensiveType x = std::move(vec.back()); vec.pop_back(); 

SomeExpensiveType Suponiendo tiene un constructor movimiento. (y obviamente cierto para su caso)

+0

Pensé en eso, pero soy cauteloso al mover algo del contenedor de esa manera. Ahora que lo pienso más, parece perfectamente legal ...: | – GManNickG

+0

Hmm. No estoy seguro de que sea correcto. Seguramente vec.back() tendría que tener la semántica de movimiento, es decir, devolver un && – ScaryAardvark

+0

Eso sí, lo he pensado un poco más y es posible que tengas razón. – ScaryAardvark

-1

Por lo general, para tipos costosos, creo que le conviene introducir una clase contenedora o un puntero inteligente en el contenedor. De esta forma, evitará copias caras y, en cambio, solo hará copias baratas del puntero inteligente o de la clase contenedora. También puedes usar punteros sin procesar si quieres jaja.

class ExpensiveWrapper 
{ 
public: 
    ExpensiveWrapper(ExpensiveClass* in) { mPtr = in; } 

    // copy constructors here.... 

private: 
    ExpensiveWrapper* mPtr; 

}; 
+3

Un punto de semántica de "movimiento" es deshacerse de este método. Los contenedores solo 'moverán' sus contenidos en lugar de copiarlos. – GManNickG

4

Para completar (y cualquier tropiezo en esta pregunta sin un compilador de C++ 1x), una alternativa que ya existe en la actualidad:

SomeExpensiveType x; 
std::swap(x, vec.back()); 
vec.pop_back(); 

Sólo requiere una especialización de std::swap de existir para el tipo de elemento.

+3

Excepto que no use std :: swap explícitamente (ya que no se puede especializar adecuadamente para muchos tipos); use una declaración de uso con una llamada de intercambio no calificada. –

+1

@Roger Pate - o use 'boost :: swap' para obtener lo mismo. –

+0

Prefiero escribir una sola línea que preocuparme por quién se va a quejar de Boost, como alguien inevitablemente lo hace. :) –

4
template<class C> 
auto pop_back(C& c) -> typename std::decay<decltype(c.back())>::type 
{ 
    auto value (std::move(c.back())); 
    c.pop_back(); 
    return value; // also uses move semantics, implicitly 
    // RVO still applies to reduce the two moves to one 
} 
+0

Encuentro std :: decay más apropiado que std :: remove_reference. Por ejemplo, convierte un "const MyClass &" en un "MyClass" (eliminando const). – sellibitze

+0

@sellibitze: tienes razón, eso es más apropiado; ¡Gracias! –

+0

No estoy 100% seguro, pero es posible que falte un 'typename' después de' -> ' – sellibitze