Dichiarazione di riferimento Rvalue: &&

Mantiene un riferimento a un'espressione rvalue.

type-id && cast-expression

Note

I riferimenti Rvalue consentono di distinguere un valore da un rvalue.I riferimenti di Lvalue e i riferimenti rvalue siano sintatticamente e semanticamente simile, ma seguono le regole in modo diverso.Per ulteriori informazioni sui lvalue e i di valori rvalue, vedere Lvalue e di valori rvalue.Per ulteriori informazioni sui riferimenti di lvalue, vedere Dichiarazione di riferimento Lvalue: &.

Nelle sezioni seguenti viene descritto come riferimenti rvalue supportano l'implementazione della semantica di spostamento e l'inoltro perfetto di.

Semantica di spostamento

I riferimenti Rvalue supportano l'implementazione della semantica di spostamento, che possono migliorare significativamente le prestazioni delle applicazioni.La semantica di spostamento consente di scrivere codice che trasferisce le risorse (come memoria assegnata in modo dinamico) da un oggetto a un altro.Funzionamento di semantica di spostamento in quanto consente alle risorse da trasferire oggetti temporanei che non è possibile fare riferimento in un punto qualsiasi del programma.

Per implementare la semantica di spostamento, in genere si fornisce un costruttore di spostamento e facoltativamente un operatore di assegnazione di spostamento (operator=), alla classe.Copia e le operazioni di assegnazione dei database di origine sono di valori rvalue quindi automaticamente utilizzano la semantica di spostamento.A differenza di un costruttore di copia predefinito, il compilatore non fornisce un costruttore predefinito di spostamento.Per ulteriori informazioni su come scrivere un costruttore di spostamento e su come utilizzarlo in un'applicazione, vedere Procedura: Scrivere un costruttore di spostamento.

È inoltre possibile eseguire l'overload delle funzioni comuni e operatori per sfruttare la semantica di spostamento.Visual C++ 2010 introduce la semantica di spostamento nella libreria standard (STL) del modello.Ad esempio, la classe string implementa le operazioni che eseguono la semantica di spostamento.Si consideri l'esempio seguente che concatena diverse stringhe e stampare il risultato:

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

Prima Visual C++ 2010, ogni chiamata a operator+ alloca e restituisce un nuovo oggetto temporaneo string (un rvalue).operator+ non può aggiungere una stringa all'altra in quanto non sa se le stringhe di origine sono lvalue o di valori rvalue.Se le stringhe di origine sono entrambi i lvalue, è possibile fare riferimento in un punto qualsiasi del programma e pertanto non devono essere modificate.L'utilizzo di riferimenti rvalue, operator+ può essere modificato per intraprendere di valori rvalue, non è possibile fare riferimento in un punto qualsiasi del programma.Pertanto, operator+ possibile ora aggiungere una stringa a un altro.Ciò può ridurre notevolmente il numero delle allocazioni di memoria dinamiche che la classe string deve eseguire.Per ulteriori informazioni sulla classe string, vedere basic_string Class.

Spostare le guide la semantica anche quando il compilatore non può utilizzare l'ottimizzazione (RVO) di valore restituito o l'ottimizzazione denominata (NRVO) di valore restituito.In questi casi, il compilatore chiama il costruttore di spostamento se il tipo è definito.Per ulteriori informazioni sull'ottimizzazione denominata di valore restituito, vedere ottimizzazione denominata di valore restituito in Visual C++ 2005.

Per comprendere meglio la semantica di spostamento, si consideri l'esempio di inserimento di elementi in un oggetto vector.Se la capacità dell'oggetto vector consentita, l'oggetto vector deve allocare memoria per i relativi elementi e copiare ogni elemento in un'altra posizione di memoria per fare spazio all'elemento inserito.Quando un'operazione di inserimento copia un elemento, creare un nuovo elemento, chiama il costruttore di copia per copiare i dati dall'elemento precedente al nuovo elemento e quindi eliminato l'elemento precedente.La semantica di spostamento consente di spostare gli oggetti direttamente senza dover effettuare l'allocazione della memoria e le operazioni di copia onerose.

Per sfruttare la semantica di spostamento nell'esempio vector, è possibile scrivere un costruttore di spostamento ai dati di uscire da un oggetto a un altro.

Per ulteriori informazioni sull'introduzione della semantica di spostamento nella libreria STL in Visual C++ 2010, vedere Riferimento della libreria C++ standard.

Perfezioni l'inoltro

L'inoltro perfetto riduce la necessità di funzioni in overload e consente di evitare il problema di inoltro.Il problema di inoltro può verificarsi quando si scrive una funzione generica che i riferimenti di accetta come i relativi parametri e passa avanti(o) questi parametri a un'altra funzione.Ad esempio, se la funzione generica accetta un parametro di tipo const T&, la funzione chiamata non può modificare il valore del parametro.Se la funzione generica accetta un parametro di tipo T&, la funzione non può essere chiamato utilizzando un rvalue ad esempio un oggetto o temporaneo un valore letterale Integer).

In genere, risolvere questo problema, è necessario fornire versioni di overload della funzione generica che accettano sia T& che const T& per ogni parametro.Di conseguenza, il numero delle funzioni in overloadcon il numero di parametri.I riferimenti Rvalue consentono di scrivere una versione di una funzione che accetta argomenti arbitrari e li inoltri a un'altra funzione come se un'altra funzione è stata chiamata direttamente.

Si consideri l'esempio seguente che dichiara quattro tipi, W, X, Ye Z.Il costruttore per ogni tipo utilizza una combinazione diversa const e i riferimenti non lvalue diconst come parametri.

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

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

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

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

Si supponga di voler scrivere una funzione generica che genera oggetti.Nell'esempio seguente viene illustrato come scrivere questa funzione:

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

Nell'esempio seguente viene illustrata una chiamata alla funzione valida factory :

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

Tuttavia, nell'esempio non contiene una chiamata valida alla funzione factory perché factory accetta i riferimenti di lvalue che sono modificabili come parametri, ma vengono chiamati utilizzando i di valori rvalue:

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

In genere, risolvere questo problema, è necessario creare una versione di overload della funzione factory per ogni combinazione A& e di parametri const A&.I riferimenti Rvalue consentono di scrivere una versione della funzione factory, come illustrato nel seguente esempio:

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

In questo esempio vengono utilizzati i riferimenti rvalue mentre i parametri a factory esecuzione.Lo scopo della funzione std::forward è di inoltrare i parametri di funzione factory al costruttore della classe modello.

Nell'esempio seguente viene illustrata la funzione main che utilizza la funzione modificata factory per creare istanze W, X, Ye le classi Z.Rivista la funzione factory inoltra i parametri (lvalue o di valori rvalue) al costruttore di classe appropriato.

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

Proprietà aggiuntive dei riferimenti Rvalue

È possibile eseguire l'overload di una funzione utilizzi un riferimento lvalue e un riferimento rvalue.

Esegue l'overload di una funzione utilizzi un riferimento lvalue const o un riferimento rvalue, è possibile scrivere codice che distingue tra oggetti non modificabili (lvalue) e i valori modificabili temporanei di valori rvalue ().È possibile passare un oggetto a una funzione che accetta un riferimento rvalue a meno che l'oggetto sia contrassegnato come const.Nell'esempio seguente viene illustrata la funzione f, ovvero in overload per accettare un riferimento lvalue e un riferimento rvalue.Le chiamate di funzione fmain con i lvalue che un 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());
}

Questo esempio produce il seguente output:

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

In questo esempio, la prima chiamata a f passa una variabile locale (un valore) come argomento.La seconda chiamata a f passa un oggetto temporaneo come argomento.Poiché l'oggetto temporaneo non è possibile fare riferimento in un punto qualsiasi del programma, la chiamata viene associata alla versione di overload f che accetta un riferimento rvalue, ovvero può modificare l'oggetto.

Il compilatore considera un riferimento rvalue denominato come lvalue e un riferimento senza nome rvalue come rvalue.

Quando si scrive una funzione che accetta un riferimento rvalue come parametro, il parametro viene considerato come lvalue nel corpo della funzione.Il compilatore considera un riferimento rvalue denominato come lvalue poiché un oggetto denominato è possibile fare riferimento a diverse parti di un programma; sarebbe pericoloso concedere più parti di un programma per modificare o rimuovere le risorse di tale oggetto.Ad esempio, se più parti di un programma consente di trasferire le risorse dallo stesso oggetto, solo la prima parte correttamente trasferirà la risorsa.

Nell'esempio seguente viene illustrata la funzione g, ovvero in overload per accettare un riferimento lvalue e un riferimento rvalue.La funzione f accetta un riferimento rvalue il parametro (denominato un riferimento rvalue) e restituisce un riferimento rvalue a un riferimento senza nome rvalue).Nella chiamata a g da f, la risoluzione dell'selezionare la versione g che accetta un riferimento lvalue poiché il corpo f considera il parametro come lvalue.Nella chiamata a g da main, la risoluzione dell'selezionare la versione g che accetta un riferimento rvalue perché f restituisce un riferimento 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()));
}

Questo esempio produce il seguente output:

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

In questo esempio, la funzione main passa un valore rvalue a f.Il corpo f considera il parametro denominato come lvalue.La chiamata da f a g associa il parametro a un riferimento lvalue (versione prima di overload g).

  • È possibile eseguire il cast di un valore a un riferimento rvalue.

La funzione della libreria STL std::move consente di convertire un oggetto a un riferimento rvalue a tale oggetto.In alternativa, è possibile utilizzare la parola chiave static_cast per eseguire il cast di un valore a un riferimento rvalue, come illustrato nel seguente esempio:

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

Questo esempio produce il seguente output:

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

 

I modelli di funzione deducono i tipi di argomento di modello e quindi utilizzano regole comprimere di riferimento.

È normale scrivere un modello di funzione che passa tra (o) inoltrai parametri a un'altra funzione.È importante capire come funziona la deduzione del tipo di modello per i modelli di funzioni che accettano i riferimenti rvalue.

Se l'argomento della funzione è un rvalue, il compilatore deduce l'argomento sia un riferimento rvalue.Ad esempio, se si passa un riferimento rvalue a un oggetto di tipo X a una funzione di modello che accetta digitare T&& come parametro, la deduzione di argomento di modello deduce T per essere X.Di conseguenza, il parametro è di tipo X&&.Se l'argomento della funzione è un valore o lvalue const, il compilatore deduce il tipo sia un riferimento lvalue o riferimento lvalue const di quel tipo.

Nell'esempio seguente viene dichiarato un modello di progettazione e quindi lo specializza per diversi tipi di riferimento.La funzione print_type_and_value accetta un riferimento rvalue come parametro e lo inoltra alla versione specializzata appropriata del metodo S::print.La funzione main vengono illustrate varie modalità chiamare il metodo 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());
}

Questo esempio produce il seguente output:

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

Per risolvere ogni chiamata alla funzione print_type_and_value, il compilatore innanzitutto esegue la deduzione di argomento di modello.Il compilatore quindi applicare le regole di assemblaggio di riferimento quando sostituisce gli argomenti di template dedotti per i tipi di parametro.Ad esempio, passare la variabile locale s1 alla funzione print_type_and_value induce il compilatore a generare la firma della funzione:

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

Comprimere di riferimento del compilatore utilizza regole per ridurre la firma al seguente:

print_type_and_value<string&>(string& t)

Questa versione della funzione print_type_and_value quindi inoltra il parametro alla versione specializzata corretta del metodo S::print.

Nella tabella seguente viene riepilogato il riferimento che assembla le regole per la deduzione del tipo di argomento di modello:

Tipo esteso

Tipo compresso

T& &

T&

T& &&

T&

T&& &

T&

T&& &&

T&&

La deduzione di argomento di modello è un elemento importante di distribuire l'inoltro perfetto.L'inoltro perfetto la sezione, che viene presentato più indietro in questo argomento, vengono descritti più dettagliatamente l'inoltro perfetto.

I riferimenti Rvalue distingue dai lvalue di valori rvalue.Consentono di migliorare le prestazioni delle applicazioni eliminando la necessità delle allocazioni di memoria e le operazioni di copia non necessari.Consentono inoltre di scrivere una versione di una funzione che accetta argomenti arbitrari e li inoltri a un'altra funzione come se un'altra funzione è stata chiamata direttamente.

Vedere anche

Attività

Procedura: Scrivere un costruttore di spostamento

Riferimenti

Espressioni con gli operatori unari

Dichiarazione di riferimento Lvalue: &

Lvalue e di valori rvalue

move

forward

Altre risorse

Riferimento della libreria C++ standard