Declarador de referencia Rvalue: &&

Contiene una referencia a una expresión rvalue.

type-id && cast-expression

Comentarios

Las referencias Rvalue permiten distinguir un valor l de un valor rvalue.Las referencias de valor l y las referencias rvalue son sintácticamente y semánticamente similar, pero sigue reglas ligeramente distintas.Para obtener más información sobre son y valores r, vea Son y valores r.Para obtener más información acerca de las referencias de l, vea Declarador de referencias de valor l: y.

Las secciones siguientes describen cómo las referencias rvalue admiten la implementación de la semántica de transferencia de recursos y de reenvío directo.

Semántica de movimiento

Las referencias Rvalue admiten la implementación de la semántica de movimiento, que puede aumentar significativamente el rendimiento de las aplicaciones.Permisos de la semántica de movimiento que escriba el código que transfiere los recursos (como memoria asignada dinámicamente) de un objeto a otro.La semántica de movimiento funciona porque habilita los recursos que se van a transferir de objetos temporales que no se puede hacer referencia a cualquier parte del programa.

Para implementar la semántica de movimiento, normalmente se proporcionan un constructor de movimiento, y opcionalmente un operador de asignación de movimiento (operator=), a la clase.Las operaciones de copia y asignar cuyos orígenes son valores r a automáticamente aprovechan la semántica de transferencia de recursos.A diferencia del constructor de copias predeterminado, el compilador no proporciona un constructor predeterminado de movimiento.Para obtener más información sobre cómo escribir un constructor de movimiento y cómo utilizarlo en la aplicación, vea Cómo: escriba un movimiento Constructor.

También puede sobrecargar funciones normales y operadores para aprovechar la semántica de transferencia de recursos.Visual C++ 2010 presenta la semántica de transferencia de recursos en la biblioteca estándar (STL) de la plantilla.Por ejemplo, la clase de string implementa las operaciones que realizan la semántica de transferencia de recursos.Considere el ejemplo siguiente que concatena varias cadenas y se imprime el 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 llamada a operator+ asigna y devuelve un nuevo objeto temporal de string (un valor r).operator+ no anexa una cadena a otra porque no sabe si las cadenas de origen son son o valores r.Si las cadenas de origen son ambos son, puede que se hace referencia a otra parte del programa y por consiguiente no se deben modificar.Mediante referencias rvalue, operator+ se puede modificar para tomar los valores r, que no pueden hacer referencia a cualquier parte del programa.Por consiguiente, operator+ ahora puede anexar una cadena a otra.Esto puede reducir significativamente el número de asignaciones de memoria dinámica que la clase de string debe realizar.Para obtener más información sobre la clase string, vea basic_string Class.

Facilita la semántica de transferencia de recursos también cuando el compilador no puede utilizar la optimización (RVO) de valor devuelto o la optimización denominada (NRVO) del valor devuelto.En estos casos, el compilador de llama al constructor de movimiento si el tipo se define.Para obtener más información sobre la optimización denominada del valor devuelto, vea Optimización denominada el valor devuelto en Visual C++ 2005.

Para comprender mejor la semántica de movimiento, considere el ejemplo de insertar un elemento en un objeto de vector .Si la capacidad del objeto de vector se supera, el objeto de vector debe reasignar la memoria para los elementos y copiar a continuación cada elemento a otra ubicación de la memoria para hacer sitio para el elemento insertado.Cuando una operación de inserción copia un elemento, crea un nuevo elemento, llama al constructor de copia para copiar los datos del elemento anterior al nuevo elemento, y después destruye el elemento anterior.Los permisos de la semántica de transferencia de recursos permite mover objetos directamente sin tener que realizar operaciones costosas de asignación de memoria y copiar.

Para aprovechar la semántica de transferencia de recursos en el ejemplo de vector , puede escribir un constructor move a los datos de movimiento a partir de un objeto a otro.

Para obtener más información sobre la introducción de semántica de movimiento en STL en Visual C++ 2010, vea Referencia de la biblioteca estándar de C++.

Perfeccione el reenvío

Reenvío directo reduce la necesidad de funciones sobrecargadas y ayuda a evitar el problema de reenvío.El problema de reenvío puede producirse cuando se escribe una función genérica que las referencias de las tomas como parámetros y él pasan (o reenvío) estos parámetros a otra función.Por ejemplo, si la función genérica toma un parámetro de const T&tipo, la función llamada no puede modificar el valor de dicho parámetro.Si la función genérica toma un parámetro de T&tipo, la función no se puede llamar mediante un valor r (como un objeto temporal o un literal entero).

Normalmente, resolver este problema, debe proporcionar las versiones sobrecargadas de función genérica que toman T& y const T& para cada uno de sus parámetros.Como resultado, el número de funciones sobrecargadas aumenta exponencialmente con el número de parámetros.Las referencias Rvalue permiten escribir una versión de una función que acepta argumentos arbitrarios y que transmita otra función como si otra función se hubiera denominada directamente.

Considere el ejemplo siguiente que declara cuatro tipos, W, X, Y, y Z.El constructor para cada tipo toma otra combinación de const y referencias no de valor l deconst como 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&) {}
};

Suponga que desea escribir una función genérica que genera objetos.El ejemplo siguiente se muestra una manera de escribir esta función:

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

El ejemplo siguiente muestra una llamada completa a la función de factory :

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

Sin embargo, el ejemplo siguiente no contiene una llamada completa a la función de factory porque factory toma las referencias de valor l que sean modificables como parámetros, pero se denominan utilizando valores r:

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

Normalmente, resolver este problema, debe crear una versión sobrecargada de la función de factory para cada combinación de A& y parámetros de const A& .Las referencias Rvalue permiten escribir una versión de la función de factory , como se muestra en el ejemplo siguiente:

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 ejemplo utiliza referencias rvalue cuando los parámetros a factory .El propósito de la función de std::forward es reenviar los parámetros de la función de generador al constructor de la clase de plantilla.

El ejemplo siguiente se muestra la función de main que utiliza la función revisada de factory para crear instancias de W, de X, de Y, y las clases de Z .La función revisada de factory reenvía los parámetros (los son o los valores r) al constructor de clase adecuado.

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;
}

Propiedades adicionales de las referencias Rvalue

Puede sobrecargar una función tome una referencia de valor l y una referencia rvalue.

Sobrecarga una función tome una referencia de valorl de const o una referencia rvalue, puede escribir código que distingue los objetos no modificable (son) y valores temporales modificables (valores r).Puede pasar un objeto a una función que toma una referencia rvalue a menos que el objeto se marca como const.El ejemplo siguiente se muestra la función f, que se sobrecarga para tomar una referencia de valor l y una referencia rvalue.Las llamadas de función f de main con son y un valor 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());
}

Este ejemplo produce el siguiente resultado.

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

En este ejemplo, la primera llamada a f pasa una variable local (un valor l) como argumento.La segunda llamada a f pasa un objeto temporal como argumento.Dado que el objeto temporal no se puede hacer referencia a otra parte del programa, la llamada se enlaza a la versión sobrecargada de f que toma una referencia rvalue, que es libre de modificar el objeto.

El compilador trata una referencia denominada rvalue como valor l y una referencia sin nombre rvalue como valor r.

Cuando se escribe una función que toma una referencia rvalue como parámetro, ese parámetro se trata como valor l en el cuerpo de la función.El compilador trata una referencia denominada rvalue como valor l porque un objeto con nombre se puede hacer referencia por varias partes de un programa; sería peligroso permitir que las varias partes de un programa modificar o quitar recursos de ese objeto.Por ejemplo, si varias partes de un intento del programa para transferir recursos igual se oponen, sólo la primera parte transferirá correctamente el recurso.

El ejemplo siguiente se muestra la función g, que se sobrecarga para tomar una referencia de valor l y una referencia rvalue.La función f toma una referencia rvalue como parámetro (una referencia denominada rvalue) y especificado una referencia rvalue (una referencia sin nombre rvalue).En la llamada a g de f, la resolución de sobrecarga selecciona la versión de g que toma una referencia de valor l porque el cuerpo de f trata el parámetro como valor l.En la llamada a g de main, la resolución de sobrecarga selecciona la versión de g que toma una referencia rvalue porque f devuelve una referencia 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()));
}

Este ejemplo produce el siguiente resultado.

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

En este ejemplo, la función de main pasa un valor r a f.El cuerpo de f trata el parámetro con nombre como valor l.La llamada de f a g enlaza el parámetro a una referencia de valor l (la primera versión sobrecargada de g).

  • Puede convertir un valor l a una referencia rvalue.

La función STL std::move permite convertir un objeto en una referencia rvalue a ese objeto.Alternativamente, puede utilizar la palabra clave de static_cast para convertir un valor l a una referencia rvalue, como se muestra en el ejemplo siguiente:

// 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));
}

Este ejemplo produce el siguiente resultado.

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

 

Plantillas de función deducen sus tipos de argumento de plantilla y usan la referencia contraído reglas.

Es común escribir una plantilla de función que pase (o reenvío) sus parámetros a otra función.Es importante entender cómo la plantilla escribe los trabajos de la deducción para plantillas de función que toman referencias rvalue.

Si el argumento de la función es un valor r, el compilador deduce el argumento sea una referencia rvalue.Por ejemplo, si se pasa una referencia rvalue a un objeto de X tipo a una función de plantilla que las tomas escriben T&& como parámetro, la deducción de argumento de plantilla deduce T para ser X.Por consiguiente, el parámetro tiene el tipo X&&.Si el argumento de la función es un valor l o l de const , el compilador deduce el tipo para ser una referencia de valor l o referencia de valorl de const de ese tipo.

El ejemplo siguiente declara una plantilla de estructura y después la especial para los diferentes tipos de referencia.La función de print_type_and_value toma una referencia rvalue como parámetro y le transmite la versión especializada de método adecuada de S::print .La función de main muestra las distintas maneras de llamar al 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());
}

Este ejemplo produce el siguiente resultado.

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

Para resolver cada llamada a la función de print_type_and_value , el compilador realiza primero la deducción de argumento de plantilla.El compilador a continuación aplica referencia contraído reglas cuando sustituye los argumentos deducidos de plantilla para los tipos de parámetro.Por ejemplo, pase la variable local s1 a la función de print_type_and_value hace que el compilador genere la signatura siguiente de la función:

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

Las aplicaciones del compilador hace referencia contraer reglas para reducir la signatura al siguiente:

print_type_and_value<string&>(string& t)

Esta versión de la función de print_type_and_value después transmite al parámetro la versión especializada correcta del método de S::print .

La tabla siguiente se resume la referencia que contrae las reglas para la deducción del tipo de argumento de plantilla:

Tipo expandido

Tipo contraído

T& &

T&

T& &&

T&

T&& &

T&

T&& &&

T&&

La deducción de argumento de plantilla es un elemento importante de implementar reenvío directo.El reenvío de Perfect section, que se muestra anteriormente en este tema, se describe reenvío directo con más detalle.

Resumen

Las referencias Rvalue distinguen son de valores r.Pueden ayudarle a mejorar el rendimiento de las aplicaciones que elimina la necesidad de asignaciones de memoria y operaciones innecesarias de copia.También permiten escribir una versión de una función que acepta argumentos arbitrarios y que transmita otra función como si otra función se hubiera denominada directamente.

Vea también

Tareas

Cómo: escriba un movimiento Constructor

Referencia

Expresiones con los operadores unarios

Declarador de referencias de valor l: y

Son y valores r

move

forward

Otros recursos

Referencia de la biblioteca estándar de C++