Ambos std::forward
e std::move
nada mais são do que elencos.
X x;
std::move(x);
O exemplo acima converte a expressão lvalue x
do tipo X para uma expressão rvalue do tipo X (um valor x para ser exato). move
também pode aceitar um rvalue:
std::move(make_X());
e, nesse caso, é uma função de identidade: pega um rvalor do tipo X e retorna um rvalor do tipo X.
Com std::forward
você pode selecionar o destino até certo ponto:
X x;
std::forward<Y>(x);
Converte a expressão lvalue x
do tipo X para uma expressão do tipo Y. Há restrições sobre o que Y pode ser.
Y pode ser uma base acessível de X, ou uma referência a uma base de X. Y pode ser X ou uma referência a X. Não é possível descartar qualificadores cv com forward
, mas é possível adicionar qualificadores cv. Y não pode ser um tipo que é meramente conversível em X, exceto por meio de uma conversão Base acessível.
Se Y for uma referência lvalue, o resultado será uma expressão lvalue. Se Y não for uma referência de lvalue, o resultado será uma expressão de rvalue (xvalue, para ser preciso).
forward
pode aceitar um argumento rvalue apenas se Y não for uma referência lvalue. Ou seja, você não pode converter um rvalue em lvalue. Por razões de segurança, isso geralmente leva a referências pendentes. Mas converter um rvalue para rvalue é aceitável e permitido.
Se você tentar especificar Y para algo que não é permitido, o erro será detectado no tempo de compilação, não no tempo de execução.