Compartilhar via


Declarador de referência Rvalue: & &

Contém uma referência a uma expressão de rvalue.

type-id && cast-expression

Comentários

Referências de Rvalue permite que você distinguir um lvalue de um rvalue.Referências de Lvalue e referências de rvalue é sintaticamente e semanticamente semelhantes, mas seguem as regras um pouco diferentes.Para obter mais informações sobre lvalues e de rvalues, consulte Lvalues e Rvalues.Para obter mais informações sobre referências de lvalue, consulte Declarador de referência lvalue: &.

As seções a seguir descrevem como referências de rvalue oferecem suporte a implementação de semântica de animação e de transferência perfeita.

Semântica de animação

Referências de Rvalue oferecem suporte a implementação de semântica de animação, que pode aumentar significativamente o desempenho de seus aplicativos.A semântica de animação permite que você escreva código que transfere recursos (como a memória alocada dinamicamente) de um objeto para outro.A semântica de movimentação funciona porque ela permite que os recursos a ser transferidos de objetos temporários que não podem ser referenciados em outro lugar no programa.

Para implementar a semântica de animação, você normalmente fornece um construtor de mover, e opcionalmente um operador de atribuição de movimentação (operator=), a sua classe.Operações de impressão e de atribuição cujas fontes são rvalues então automaticamente colunas aproveitam-se de semântica de animação.A o contrário do construtor de impressão padrão, o compilador não fornecer um construtor padrão de animação.Para obter mais informações sobre como escrever um construtor de animação e como usá-lo em seu aplicativo, consulte Como: gravar um construtor de movimentação.

Você também pode sobrecarregar funções comuns e operadores para aproveitar a semântica de animação.Visual C++ 2010 apresenta a semântica de movimentação na biblioteca padrão (STL) do modelo.Por exemplo, a classe de string implementa as operações que executam a semântica de animação.Considere o seguinte exemplo que concatena várias cadeias de caracteres e imprime o resultado:

// string_concatenation.cpp
// compile with: /EHsc
#include <iostream>
#include <string>
using namespace std;

int main()
{
   string s = string("h") + "e" + "ll" + "o";
   cout << s << endl;
}

Antes de Visual C++ 2010, cada chamada a operator+ aloca e retorna um novo objeto temporário de string (um rvalue).operator+ não pode acrescentar uma cadeia de caracteres para outro porque não souber se as cadeias de caracteres de origem são lvalues ou rvalues.Se as cadeias de caracteres de origem são ambos os lvalues, podem ser referenciadas em outro lugar no programa e portanto devem ser alteradas.Usando referências de rvalue, operator+ pode ser alterado para extrair os rvalues, que não podem ser referenciados em outro lugar no programa.Portanto, operator+ agora pode acrescentar uma cadeia de caracteres para outra.Isso pode reduzir significativamente o número de alocações de memória dinâmicos que a classe de string deve executar.Para obter mais informações sobre a classe string, consulte basic_string Class.

A semântica de movimentação também ajuda quando o compilador não pode usar a otimização (RVO) do valor de retorno ou a otimização chamado (NRVO) do valor de retorno.Em esses casos, o compilador chama o construtor de movimentação se o tipo o define.Para obter mais informações sobre otimização de chamada do valor de retorno, consulte Otimização chamado de valor de retorno no Visual C++ 2005.

Para compreender melhor as semânticas de animação, considere o exemplo de inserir um elemento em um objeto de vector .Se a capacidade do objeto de vector é excedida, o objeto de vector deve realocar a memória para seus elementos e então copiar cada elemento para outro local da memória para fazer espaço para o elemento inserido.Quando uma operação de inserção copia um elemento, cria um novo elemento, chama o construtor para copiar os dados do elemento anterior para o novo elemento for, e o elemento anterior.A semântica de animação permite que você mova objetos diretamente sem precisar executar operações faces de alocação de memória e de impressão.

Para aproveitar a semântica de movimentação no exemplo de vector , você pode escrever um construtor de mover os dados de movimentação de um objeto para outro.

Para obter mais informações sobre a entrada de semântica de movimentação no STL em Visual C++ 2010, consulte Referência da biblioteca C++ padrão.

Aperfeiçoe a transmissão

Encaminhamento perfeita reduz a necessidade de funções sobrecarregadas e ajuda a evitar que o problema de transferência.O problema de transferência pode ocorrer quando você escrever uma função genérico que as referências de utiliza como seus parâmetros e ele passa (ou) encaminhaesses parâmetros para outra função.Por exemplo, se a função genérico recebe um parâmetro de tipo const T&, então a função chamada não pode alterar o valor do parâmetro.Se a função genérico recebe um parâmetro de tipo T&, então a função não pode ser chamada usando um rvalue (como um literal temporário do objeto ou inteiro).

Normalmente, para resolver esse problema, você deve fornecer as versões sobrecarregadas de função genérico que têm T& e const T& para cada um dos seus parâmetros.Como resultado, o número de funções sobrecarregadas aumenta exponencialmente com o número de parâmetros.Referências de Rvalue permite que você escreva uma versão de uma função que aceita arbitrário e os argumentos na outra função como se fosse outra função chamada diretamente.

Considere o seguinte exemplo que declara quatro tipos, W, X, Y, e Z.O construtor para cada tipo usa uma combinação diferente de const e referências não de lvalue deconst como seus parâmetros.

struct W
{
   W(int&, int&) {}
};

struct X
{
   X(const int&, int&) {}
};

struct Y
{
   Y(int&, const int&) {}
};

struct Z
{
   Z(const int&, const int&) {}
};

Suponha que você deseja para gravar uma função genérico que gera objetos.O exemplo a seguir mostra uma maneira para escrever essa função:

template <typename T, typename A1, typename A2>
T* factory(A1& a1, A2& a2)
{
   return new T(a1, a2);
}

O exemplo seguinte mostra uma chamada para a função válida de factory :

int a = 4, b = 5;
W* pw = factory<W>(a, b);

Em o entanto, o exemplo não contém uma chamada para a função de factory válido porque factory leva as referências de lvalue que são modificáveis como seus parâmetros, mas são chamadas usando rvalues:

Z* pz = factory<Z>(2, 2);

Normalmente, para resolver esse problema, você deve criar uma versão sobrecarregada de função de factory para cada combinação de A& e de parâmetros de const A& .Referências de Rvalue permite que você escreva uma versão de função de factory , conforme mostrado no exemplo o seguir:

template <typename T, typename A1, typename A2>
T* factory(A1&& a1, A2&& a2)
{
   return new T(std::forward<A1>(a1), std::forward<A2>(a2));
}

Este exemplo usa referências de rvalue como os parâmetros para factory funcionam.O objetivo de função de std::forward é encaminhar os parâmetros da função de fábrica para o construtor da classe de modelo.

O exemplo a seguir mostra a função de main usando a função de factory revisada para criar instâncias de W, de X, de Y, e as classes de Z .A função revisada de factory encaminha seus parâmetros (lvalues ou rvalues) para o construtor de classe apropriado.

int main()
{
   int a = 4, b = 5;
   W* pw = factory<W>(a, b);
   X* px = factory<X>(2, b);
   Y* py = factory<Y>(a, 2);
   Z* pz = factory<Z>(2, 2);

   delete pw;
   delete px;
   delete py;
   delete pz;
}

Propriedades adicionais de referências de Rvalue

Você pode sobrecarregar uma função para obter uma referência de lvalue e uma referência de rvalue.

Sobrecarga de uma função para obter uma referência de lvalue de const ou uma referência de rvalue, você pode escrever código que faz distinção entre objetos não modificáveis (lvalues) e valores temporários modificáveis (rvalues).Você pode passar um objeto a uma função que tem uma referência de rvalue a menos que o objeto está marcado como const.O exemplo a seguir mostra a função f, que é sobrecarregada para obter uma referência de lvalue e uma referência de rvalue.As chamadas de função f de main com lvalues e um rvalue.

// reference-overload.cpp
// Compile with: /EHsc
#include <iostream>
using namespace std;

// A class that contains a memory resource.
class MemoryBlock
{
   // TODO: Add resources for the class here.
};

void f(const MemoryBlock&)
{
   cout << "In f(const MemoryBlock&). This version cannot modify the parameter." << endl;
}

void f(MemoryBlock&&)
{
   cout << "In f(MemoryBlock&&). This version can modify the parameter." << endl;
}

int main()
{
   MemoryBlock block;
   f(block);
   f(MemoryBlock());
}

Esse exemplo produz a seguinte saída.

In f(const MemoryBlock&). This version cannot modify the parameter.
In f(MemoryBlock&&). This version can modify the parameter.

Em esse exemplo, a primeira chamada para f passa uma variável local (um lvalue) como seu argumento.A segunda chamada a f passa um objeto temporário como seu argumento.Porque o objeto temporário não pode ser referenciado em outro lugar no programa, a chamada associação à versão sobrecarregada de f que utiliza uma referência de rvalue, que é livre alterar o objeto.

O compilador trata uma referência chamado de rvalue como um lvalue e uma referência sem nome de rvalue como um rvalue.

Quando você escrever uma função que tem uma referência de rvalue como seu parâmetro, esse parâmetro é tratado como um lvalue no corpo da função.O compilador trata uma referência chamado de rvalue como um lvalue como um objeto chamado pode ser referenciado por várias partes de um programa; seria perigoso permitir que várias partes de um programa modificar ou remover recursos do objeto.Por exemplo, se várias partes de um programa tenta transferir recursos do mesmo objeto, a primeira parte transferirá com êxito o recurso.

O exemplo a seguir mostra a função g, que é sobrecarregada para obter uma referência de lvalue e uma referência de rvalue.A função f utiliza uma referência de rvalue como seu parâmetro nomeado (uma referência de rvalue) e retorna uma referência de rvalue (uma referência sem nome de rvalue).Em a chamada a g de f, a resolução de sobrecarga seleciona a versão de g que utiliza uma referência de lvalue porque o corpo de f trata o parâmetro como um lvalue.Em a chamada a g de main, a resolução de sobrecarga seleciona a versão de g que utiliza uma referência de rvalue porque f retorna uma referência de rvalue.

// named-reference.cpp
// Compile with: /EHsc
#include <iostream>
using namespace std;

// A class that contains a memory resource.
class MemoryBlock
{
   // TODO: Add resources for the class here.
};

void g(const MemoryBlock&) 
{
   cout << "In g(const MemoryBlock&)." << endl;
}

void g(MemoryBlock&&) 
{
   cout << "In g(MemoryBlock&&)." << endl;
}

MemoryBlock&& f(MemoryBlock&& block)
{
   g(block);
   return block;
}

int main()
{
   g(f(MemoryBlock()));
}

Esse exemplo produz a seguinte saída.

In g(const MemoryBlock&).
In g(MemoryBlock&&).

Em esse exemplo, a função de main passa um rvalue a f.O corpo de f trata o parâmetro nomeado como um lvalue.A chamada de f a g associa o parâmetro como uma referência de lvalue (a primeira versão sobrecarregada de g).

  • Você pode converter um lvalue a uma referência de rvalue.

A função de STL std::move permite que você converter um objeto para um referência de rvalue ao objeto.Como alternativa, você pode usar a palavra-chave de static_cast para converter um lvalue a uma referência de rvalue, conforme mostrado no exemplo o seguir:

// cast-reference.cpp
// Compile with: /EHsc
#include <iostream>
using namespace std;

// A class that contains a memory resource.
class MemoryBlock
{
   // TODO: Add resources for the class here.
};

void g(const MemoryBlock&) 
{
   cout << "In g(const MemoryBlock&)." << endl;
}

void g(MemoryBlock&&) 
{
   cout << "In g(MemoryBlock&&)." << endl;
}

int main()
{
   MemoryBlock block;
   g(block);
   g(static_cast<MemoryBlock&&>(block));
}

Esse exemplo produz a seguinte saída.

In g(const MemoryBlock&).
In g(MemoryBlock&&).

 

Os modelos de função deduzem seus tipos de argumento de modelo e usam a referência que recolhe regras.

É comum escrever um modelo de função que passa (ou) encaminhaseus parâmetros para outra função.É importante compreender como dedução do tipo de modelo funciona para os modelos de função que têm referências de rvalue.

Se o argumento da função é um rvalue, o compilador deduz o argumento para ser uma referência de rvalue.Por exemplo, se você passar uma referência de rvalue a um objeto do tipo X a uma função de modelo que utiliza digitem T&& como seu parâmetro, a dedução do argumento do modelo deduz T para ser X.Portanto, o parâmetro tem o tipo X&&.Se o argumento da função é um lvalue ou lvalue de const , o compilador deduz o tipo para ser uma referência de lvalue ou referência de lvalue de const de esse tipo.

O seguinte exemplo declara um modelo de estrutura e especializa-o em para vários tipos de referência.A função de print_type_and_value utiliza uma referência de rvalue como seu parâmetro e encaminhar-la à versão especializada apropriada do método de S::print .A função de main demonstra várias maneiras para chamar o método de S::print .

// template-type-deduction.cpp
// Compile with: /EHsc
#include <iostream>
#include <string>
using namespace std;

template<typename T> struct S;

// The following structures specialize S by 
// lvalue reference (T&), const lvalue reference (const T&), 
// rvalue reference (T&&), and const rvalue reference (const T&&).
// Each structure provides a print method that prints the type of 
// the structure and its parameter.

template<typename T> struct S<T&> {
   static void print(T& t)
   {
      cout << "print<T&>: " << t << endl;
   }
};

template<typename T> struct S<const T&> {
   static void print(const T& t)
   {
      cout << "print<const T&>: " << t << endl;
   }
};

template<typename T> struct S<T&&> {
   static void print(T&& t)
   {
      cout << "print<T&&>: " << t << endl;
   }
};

template<typename T> struct S<const T&&> {
   static void print(const T&& t)
   {
      cout << "print<const T&&>: " << t << endl;
   }
};

// This function forwards its parameter to a specialized
// version of the S type.
template <typename T> void print_type_and_value(T&& t) 
{
   S<T&&>::print(std::forward<T>(t));
}

// This function returns the constant string "fourth".
const string fourth() { return string("fourth"); }

int main()
{
   // The following call resolves to:
   // print_type_and_value<string&>(string& && t)
   // Which collapses to:
   // print_type_and_value<string&>(string& t)
   string s1("first");
   print_type_and_value(s1); 

   // The following call resolves to:
   // print_type_and_value<const string&>(const string& && t)
   // Which collapses to:
   // print_type_and_value<const string&>(const string& t)
   const string s2("second");
   print_type_and_value(s2);

   // The following call resolves to:
   // print_type_and_value<string&&>(string&& t)
   print_type_and_value(string("third"));

   // The following call resolves to:
   // print_type_and_value<const string&&>(const string&& t)
   print_type_and_value(fourth());
}

Esse exemplo produz a seguinte saída.

print<T&>: first
print<const T&>: second
print<T&&>: third
print<const T&&>: fourth

Para resolver cada chamada para a função de print_type_and_value , o compilador executa primeiro a dedução do argumento do modelo.O compilador aplica a referência que recolhe regras quando substitui os argumentos deduzidos de modelo para os tipos de parâmetro.Por exemplo, passe a variável local s1 à função de print_type_and_value faz com que o compilador gere a seguir assinatura de função:

print_type_and_value<string&>(string& && t)

O compilador referenciam recolher regras para reduzir a assinatura a seguir:

print_type_and_value<string&>(string& t)

Esta versão da função de print_type_and_value encaminha em seu parâmetro para a versão especializada correta do método de S::print .

A tabela a seguir resume a referência que recolhe regras para dedução do tipo de argumento de modelo:

Tipo expandido

Tipo recolhido

T& &

T&

T& &&

T&

T&& &

T&

T&& &&

T&&

A dedução do argumento do modelo é um elemento importante para implementar a transmissão perfeita.Encaminhamento perfeita a seção, que é apresentada anteriormente em este tópico descreve encaminhar perfeito, com mais detalhes.

Resumo

Referências de Rvalue distinguiem lvalues de rvalues.Podem ajudar a melhorar o desempenho de seus aplicativos eliminando a necessidade para alocações de memória e operações desnecessárias de impressão.Também permite que você escreva uma versão de uma função que aceita arbitrário e os argumentos na outra função como se fosse outra função chamada diretamente.

Consulte também

Tarefas

Como: gravar um construtor de movimentação

Referência

Expressões com operadores unários

Declarador de referência lvalue: &

Lvalues e Rvalues

move

forward

Outros recursos

Referência da biblioteca C++ padrão