c++ - ¿Es std:: move(* this) un buen patrón?
c++11 move-semantics (1)
Sí, *this
es siempre un valor l, no importa cómo se llame a una función miembro, por lo que si desea que el compilador lo trate como un valor r, debe usar std::move
o equivalente. Tiene que ser, considerando esta clase:
struct A {
void gun() &; // leaves object usable
void gun() &&; // makes object unusable
void fun() && {
gun();
gun();
}
};
Si se hace *this
un valor, esto sugiere que la primera llamada de la fun
a la gun
puede dejar el objeto inutilizable. La segunda llamada entonces fallaría, posiblemente mal. Esto no es algo que debería suceder implícitamente.
Esta es la misma razón por la que dentro de void f(T&& t)
, t
es un lvalue. A este respecto, *this
no es diferente de cualquier parámetro de función de referencia.
Para hacer que este código con calificadores de referencia de C ++ 11 funcione como se espera, debo introducir un std::move(*this)
que no suena bien.
#include<iostream>
struct A{
void gun() const&{std::cout << "gun const&" << std::endl;}
void gun() &&{std::cout << "gun&&" << std::endl;}
void fun() const&{gun();}
void fun() &&{std::move(*this).gun();} // <-- is this correct? or is there a better option
};
int main(){
A a; a.fun(); // prints gun const&
A().fun(); // prints gun&&
}
Algo no suena bien al respecto. ¿Es necesario el std::move
? ¿Es este un uso recomendado para ello? Por el momento, si no lo uso, obtengo un gun const&
en ambos casos, lo que no es el resultado esperado.
(Parece que *this
es una referencia implícita y lvalor siempre, lo que tiene sentido, pero la única forma de escapar es usar move
)
Probado con clang 3.4
y gcc 4.8.3
.
EDITAR : Esto es lo que entiendo de @hvd respuesta:
1) std::move(*this)
es sintácticamente y conceptualmente correcto
2) Sin embargo, si la gun
no es parte de la interfaz deseada, no hay razón para sobrecargar las versiones lv-ref y rv-ref de la misma. Y dos funciones con nombres diferentes pueden hacer el mismo trabajo. Después de todo, los ref-calificadores son importantes en el nivel de interfaz, que generalmente es solo la parte pública.
struct A{
private:
void gun() const{std::cout << "gun const&" << std::endl;}
void gun_rv(){std::cout << "gun called from fun&&" << std::endl;}
public:
void fun() const&{gun();}
void fun() &&{gun_rv();} // no need for `std::move(*this)`.
};
Pero, de nuevo, si gun
es parte de la interfaz (genérica), entonces std::move(*this)
es necesario, pero solo entonces. Y también, incluso si la gun
no es parte de la interfaz, existen ventajas de legibilidad al no dividir la función de la gun
como una función con dos nombres diferentes y el costo de esta es, bueno ..., std::move(*this)
.
EDIT 2 : En retrospectiva, esto es similar al caso C ++ 98 de sobrecarga const
y no const
de la misma función. En algunos casos , tiene sentido usar const_cast
(otra forma de const_cast
) para no repetir el código y tener las dos funciones con el mismo nombre ... EDIT 3 : ... https://stackoverflow.com/a/124209/225186