Eu simplesmente não entendi totalmente a lógica de std::move()
.
A princípio, pesquisei no Google, mas parece que só existem documentos sobre como usar std::move()
, não como funciona sua estrutura.
Quer dizer, eu sei o que a função de membro do modelo é, mas quando eu olho para a std::move()
definição no VS2010, ainda é confuso.
a definição de std :: move () segue abaixo.
template<class _Ty> inline
typename tr1::_Remove_reference<_Ty>::_Type&&
move(_Ty&& _Arg)
{ // forward _Arg as movable
return ((typename tr1::_Remove_reference<_Ty>::_Type&&)_Arg);
}
O que é estranho primeiro para mim é o parâmetro, (_Ty && _Arg), porque quando eu chamo a função como você vê abaixo,
// main()
Object obj1;
Object obj2 = std::move(obj1);
basicamente é igual a
// std::move()
_Ty&& _Arg = Obj1;
Mas como você já sabe, não é possível vincular diretamente um LValue a uma referência RValue, o que me faz pensar que deveria ser assim.
_Ty&& _Arg = (Object&&)obj1;
No entanto, isso é absurdo porque std :: move () deve funcionar para todos os valores.
Acho que, para entender totalmente como isso funciona, devo dar uma olhada nessas estruturas também.
template<class _Ty>
struct _Remove_reference
{ // remove reference
typedef _Ty _Type;
};
template<class _Ty>
struct _Remove_reference<_Ty&>
{ // remove reference
typedef _Ty _Type;
};
template<class _Ty>
struct _Remove_reference<_Ty&&>
{ // remove rvalue reference
typedef _Ty _Type;
};
Infelizmente, ainda é tão confuso e eu não entendo.
Eu sei que tudo isso é devido à minha falta de habilidades básicas de sintaxe sobre C ++. Gostaria de saber como funcionam bem e quaisquer documentos que eu conseguir na internet serão mais do que bem-vindos. (Se você puder explicar isso, também será incrível).
fonte
move
funciona e não como é implementado. Acho esta explicação muito útil: pagefault.blog/2018/03/01/… .Respostas:
Começamos com a função mover (que eu limpei um pouco):
Vamos começar com a parte mais fácil - isto é, quando a função é chamada com rvalue:
e nosso
move
modelo é instanciado da seguinte maneira:Uma vez que
remove_reference
converteT&
paraT
ouT&&
paraT
, eObject
não é referência, nossa função final é:Agora, você pode se perguntar: nós ainda precisamos do elenco? A resposta é: sim, temos. A razão é simples; a referência de rvalue nomeada é tratada como lvalue (e a conversão implícita de lvalue para a referência de rvalue é proibida por padrão).
Aqui está o que acontece quando chamamos
move
com lvalue:e a
move
instanciação correspondente :Novamente,
remove_reference
converteObject&
paraObject
e nós obtemos:Agora chegamos à parte complicada: o que
Object& &&
significa e como pode ser vinculado a lvalue?Para permitir o encaminhamento perfeito, o padrão C ++ 11 fornece regras especiais para recolhimento de referência, que são as seguintes:
Como você pode ver, sob essas regras,
Object& &&
na verdadeObject&
, significa que é uma referência lvalue simples que permite vincular lvalues.A função final é assim:
que não é diferente da instanciação anterior com rvalue - ambos lançam seu argumento para a referência rvalue e, em seguida, o retornam. A diferença é que a primeira instanciação pode ser usada apenas com rvalues, enquanto a segunda funciona com lvalues.
Para explicar por que precisamos de
remove_reference
um pouco mais, vamos tentar esta funçãoe instancie-o com lvalue.
Aplicando as regras de recolhimento de referência mencionadas acima, você pode ver que obtemos uma função que é inutilizável como
move
(para simplificar, você a chama com lvalue e obtém lvalue de volta). No mínimo, essa função é a função de identidade.fonte
T
comoObject&
, eu não sabia se isso estava realmente feito. Eu esperariaT
avaliar tambémObject
neste caso, pois pensei que esse era o motivo para a introdução de referências de wrapper estd::ref
, ou não era.template <typename T> void f(T arg)
(que é o que está no artigo da Wikipedia) etemplate <typename T> void f(T& arg)
. O primeiro resolve para valor (e se você quer passar referência, tem que embrulhá-lostd::ref
), enquanto o segundo sempre resolve para referência. Infelizmente, as regras para a dedução do argumento do modelo são bastante complexas, portanto, não posso fornecer um raciocínio preciso por queT&&
resovles paraObject& &&
(mas acontece de fato).template <typename T> T&& also_wanna_be_move(T& arg) { return static_cast<T&&>(arg); }
std::move
apenas lançar lvalues em rvalues, então sim,T&
tudo bem. Este truque é feito principalmente para flexibilidade: você pode chamarstd::move
tudo (rvalues incluído) e obter rvalue de volta._Ty é um parâmetro de modelo e, nesta situação
_Ty é o tipo "Objeto &"
é por isso que _Remove_reference é necessário.
Seria mais como
Se não removêssemos a referência, seria como se estivéssemos fazendo
Mas ObjectRef && se reduz a Object &, que não poderíamos vincular a obj2.
A razão pela qual ele reduz dessa forma é para suportar o encaminhamento perfeito. Veja este artigo .
fonte
_Remove_reference_
é necessário. Por exemplo, se você tiverObject&
como typedef e fizer referência a ele, ainda assim obteráObject&
. Por que isso não funciona com &&? Não é uma resposta para isso, e que tem a ver com o encaminhamento perfeito.