Eu estive examinando alguns dos novos recursos do C ++ 11 e um que eu notei é o duplo e comercial na declaração de variáveis, como T&& var
.
Para começar, como se chama essa fera? Eu gostaria que o Google nos permitisse procurar pontuação como essa.
O que exatamente isso significa?
À primeira vista, parece ser uma referência dupla (como os ponteiros duplos no estilo C T** var
), mas estou tendo dificuldade em pensar em um caso de uso para isso.
c++
c++11
rvalue-reference
c++-faq
perfect-forwarding
paxdiablo
fonte
fonte
:)
Respostas:
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 .
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:
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:
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:
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 paraf1
ef2
: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:
Se chamarmos
factory<foo>(5)
, o argumento será deduzido comoint&
, o que não será vinculado a um literal 5, mesmo sefoo
o construtor usar umint
. Bem, poderíamos usarA1 const&
, mas e sefoo
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 noA1&
e noA1 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 comostd::forward
funciona, consulte esta excelente resposta .Isso nos permite definir a função de fábrica assim:
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 valorT
l, o construtor de é chamado com um valor l. A função aprimorada de fábrica funciona devido a uma regra especial:Assim, podemos usar a fábrica assim:
Propriedades importantes de referência do rvalue :
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.std::move
chamada é necessária em:foo&& r = foo(); foo f = std::move(r);
fonte
Named rvalue references are lvalues. Unnamed rvalue references are rvalues.
; sem saber disso, lutei para entender por que as pessoas fazem issoT &&t; std::move(t);
por um longo tempo em empresas de mudanças e afins.int x; int &&rrx = x;
Não há mais longos compila no GCC)typename identity<T>::type& a
equivalente aT&
?Denota uma referência rvalue. As referências de valor serão vinculadas apenas a objetos temporários, a menos que explicitamente gerado de outra forma. Eles são usados para tornar os objetos muito mais eficientes sob certas circunstâncias e para fornecer um recurso conhecido como encaminhamento perfeito, o que simplifica bastante o código do modelo.
No C ++ 03, você não pode distinguir entre uma cópia de um lvalue não mutável e um rvalue.
No C ++ 0x, esse não é o caso.
Considere a implementação por trás desses construtores. No primeiro caso, a cadeia precisa executar uma cópia para reter a semântica do valor, o que envolve uma nova alocação de heap. No entanto, no segundo caso, sabemos de antemão que o objeto que foi passado ao nosso construtor é imediatamente devido à destruição e não precisa permanecer intocado. Podemos efetivamente trocar os ponteiros internos e não executar nenhuma cópia nesse cenário, o que é substancialmente mais eficiente. A semântica de movimentação beneficia qualquer classe que tenha cópias caras ou proibidas de recursos referenciados internamente. Considere o caso de
std::unique_ptr
- agora que nossa classe pode distinguir entre temporários e não temporários, podemos fazer a semântica da movimentação funcionar corretamente para queunique_ptr
não possa ser copiada, mas possa ser movida, o que significa questd::unique_ptr
podem ser legalmente armazenados em contêineres padrão, classificados etc., enquanto os C ++ 03std::auto_ptr
não podem.Agora, consideramos o outro uso de referências rvalue - encaminhamento perfeito. Considere a questão de vincular uma referência a uma referência.
Não consigo lembrar o que o C ++ 03 diz sobre isso, mas no C ++ 0x, o tipo resultante ao lidar com referências de rvalue é crítico. Uma referência rvalue para um tipo T, onde T é um tipo de referência, torna-se uma referência do tipo T.
Considere a função de modelo mais simples - min e max. No C ++ 03, você precisa sobrecarregar as quatro combinações de const e non-const manualmente. No C ++ 0x, é apenas uma sobrecarga. Combinado com modelos variados, isso permite o encaminhamento perfeito.
Eu deixei de fora a dedução do tipo de retorno, porque não consigo me lembrar de como isso é feito de imediato, mas esse min pode aceitar qualquer combinação de lvalues, rvalues, const lvalues.
fonte
std::forward<A>(aref) < std::forward<B>(bref)
? e eu não acho que essa definição estará correta quando você tentar avançarint&
efloat&
. Melhor soltar um modelo de formulário de tipo.O termo para
T&&
quando usado com dedução de tipo (como encaminhamento perfeito) é conhecido coloquialmente como uma referência de encaminhamento . O termo "referência universal" foi cunhado por Scott Meyers neste artigo , mas mais tarde foi alterado.Isso ocorre porque pode ser um valor r ou um valor l.
Exemplos são:
Mais discussões podem ser encontradas na resposta para: Sintaxe para referências universais
fonte
Uma referência rvalue é um tipo que se comporta de maneira semelhante à referência comum X &, com várias exceções. O mais importante é que, quando se trata de resolução de sobrecarga de função, lvalues prefere referências de lvalue no estilo antigo, enquanto rvalues prefere as novas referências de rvalue:
Então, o que é um rvalue? Qualquer coisa que não seja um valor l. Um valor l é uma expressão que se refere a um local de memória e nos permite obter o endereço desse local de memória através do operador &.
É quase mais fácil entender primeiro o que os rvalues alcançam com um exemplo:
O construtor e os operadores de atribuição foram sobrecarregados com versões que usam referências de rvalue. As referências de valor permitem que uma função se ramifique no tempo de compilação (via resolução de sobrecarga) com a condição "Estou sendo chamado em um valor l ou um valor r?". Isso nos permitiu criar operadores de construtor e atribuição mais eficientes acima, que movem recursos, em vez de copiá-los.
O compilador se ramifica automaticamente no tempo de compilação (dependendo se está sendo chamado para um lvalue ou um rvalue), escolhendo se o construtor de movimentação ou o operador de atribuição de movimentação devem ser chamados.
Resumindo: as referências rvalue permitem a semântica de movimentação (e o encaminhamento perfeito, discutido no link do artigo abaixo).
Um exemplo prático e fácil de entender é o modelo de classe std :: unique_ptr . Como um unique_ptr mantém a propriedade exclusiva do ponteiro bruto subjacente, o unique_ptr não pode ser copiado. Isso violaria sua invariante propriedade exclusiva. Portanto, eles não têm construtores de cópia. Mas eles têm construtores de movimento:
static_cast<unique_ptr<int[]>&&>(ptr)
geralmente é feito usando std :: moveUm excelente artigo explicando tudo isso e muito mais (como como os rvalues permitem o encaminhamento perfeito e o que isso significa) com muitos bons exemplos é o C ++ Rvalue References de Thomas Becker, explicado . Este post contou muito com seu artigo.
Uma introdução mais curta é Uma breve introdução às referências de valor por Stroutrup, et. al
fonte
Sample(const Sample& s)
precisa também copiar o conteúdo? A mesma pergunta para o 'operador de atribuição de cópia'.