Declara uma referência de valor (doc da proposta de normas).
Aqui está uma introdução às referências ao rvalue .
Aqui está uma fantástica análise detalhada das referências de valor de um dos desenvolvedores de bibliotecas padrão da Microsoft .
CUIDADO: o artigo vinculado no MSDN ("Rvalue References: C ++ 0x Features in VC10, Part 2") é uma introdução muito clara às referências Rvalue, mas faz declarações sobre referências Rvalue que antes eram verdadeiras no rascunho C ++ 11 padrão, mas não são verdadeiras para a final! Especificamente, diz em vários pontos que as referências rvalue podem ser vinculadas a lvalues, que antes eram verdadeiras, mas foram alteradas (por exemplo, int x; int && rrx = x; não é mais compilado no GCC)
A maior diferença entre uma referência C ++ 03 (agora chamada referência lvalue no C ++ 11) é que ela pode se ligar a um rvalue como um temporário sem precisar ser const. Portanto, essa sintaxe agora é legal:
T&& r = T();
As referências rvalue fornecem principalmente o seguinte:
Mover semântica . Agora, um construtor de movimentação e um operador de atribuição de movimentação podem ser definidos com uma referência rvalue em vez da referência const-lvalue usual. Uma movimentação funciona como uma cópia, exceto que não é obrigada a manter a fonte inalterada; de fato, geralmente modifica a fonte, de modo que não possui mais os recursos movidos. Isso é ótimo para eliminar cópias estranhas, especialmente em implementações de bibliotecas padrão.
Por exemplo, um construtor de cópia pode ter esta aparência:
foo(foo const& other)
{
this->length = other.length;
this->ptr = new int[other.length];
copy(other.ptr, other.ptr + other.length, this->ptr);
}
Se esse construtor recebeu um temporário, a cópia seria desnecessária porque sabemos que o temporário será destruído; por que não fazer uso dos recursos temporários já alocados? No C ++ 03, não há como impedir a cópia, pois não podemos determinar se passamos temporariamente. No C ++ 11, podemos sobrecarregar um construtor de movimentação:
foo(foo&& other)
{
this->length = other.length;
this->ptr = other.ptr;
other.length = 0;
other.ptr = nullptr;
}
Observe a grande diferença aqui: o construtor move realmente modifica seu argumento. Isso efetivamente "moveria" o temporário para o objeto que está sendo construído, eliminando assim a cópia desnecessária.
O construtor move seria usado para referências temporárias e para valores não constantes que são convertidos explicitamente em referências para valores usando a std::move
função (apenas realiza a conversão). O código a seguir chama o construtor move para f1
e f2
:
foo f1((foo())); // Move a temporary into f1; temporary becomes "empty"
foo f2 = std::move(f1); // Move f1 into f2; f1 is now "empty"
Encaminhamento perfeito . As referências rvalue nos permitem encaminhar corretamente argumentos para funções de modelo. Tomemos, por exemplo, esta função de fábrica:
template <typename T, typename A1>
std::unique_ptr<T> factory(A1& a1)
{
return std::unique_ptr<T>(new T(a1));
}
Se chamarmos factory<foo>(5)
, o argumento será deduzido como int&
, o que não será vinculado a um literal 5, mesmo se foo
o construtor usar um int
. Bem, poderíamos usar A1 const&
, mas e se foo
pegar o argumento do construtor por referência não-const? Para fazer uma função fábrica verdadeiramente genérico, teríamos sobrecarregar fábrica no A1&
e no A1 const&
. Isso pode ser bom se a fábrica adotar 1 tipo de parâmetro, mas cada tipo de parâmetro adicional multiplicaria a sobrecarga necessária definida por 2. Isso é rapidamente impossível de manter.
As referências rvalue corrigem esse problema, permitindo que a biblioteca padrão defina uma std::forward
função que possa encaminhar adequadamente as referências lvalue / rvalue. Para mais informações sobre como std::forward
funciona, consulte esta excelente resposta .
Isso nos permite definir a função de fábrica assim:
template <typename T, typename A1>
std::unique_ptr<T> factory(A1&& a1)
{
return std::unique_ptr<T>(new T(std::forward<A1>(a1)));
}
Agora o rvalue / lvalue-ness do argumento é preservado quando passado para T
o construtor. Isso significa que, se a fábrica é chamada com um rvalue, T
o construtor de é chamado com um rvalue. Se factory é chamado com um valor T
l, o construtor de é chamado com um valor l. A função aprimorada de fábrica funciona devido a uma regra especial:
Quando o tipo de parâmetro da função está no formato T&&
onde T
está um parâmetro do modelo e o argumento da função é um valor l do tipo A
, o tipo A&
é usado para dedução do argumento do modelo.
Assim, podemos usar a fábrica assim:
auto p1 = factory<foo>(foo()); // calls foo(foo&&)
auto p2 = factory<foo>(*p1); // calls foo(foo const&)
Propriedades importantes de referência do rvalue :
- Para resolução de sobrecarga, lvalues prefere ligação a referências lvalue e rvalues prefere ligação a referências rvalue . Por isso, os temporários preferem chamar um construtor de movimentação / operador de atribuição de movimentação em vez de um operador de construção / atribuição de cópia.
- As referências rvalue se vincularão implicitamente a rvalues e a temporários resultantes de uma conversão implícita . isto
float f = 0f; int&& i = f;
é, é bem formado porque float é implicitamente conversível em int; a referência seria a um temporário que é o resultado da conversão.
- As referências de rvalue nomeadas são lvalues. As referências rvalue sem nome são rvalues. É importante entender por que a
std::move
chamada é necessária em:foo&& r = foo(); foo f = std::move(r);