Udostępnij za pośrednictwem


Odwołanie Rvalue; niewłaściwy deklarator: & &

Przechowuje odniesienie do wyrażenia rvalue.

type-id && cast-expression

Uwagi

Rvalue odniesienia umożliwiają odróżnienia lvalue rvalue.Odwołania lValue i rvalue odwołania są syntaktycznie i semantycznie podobne, ale śledzą nieco różnych zasad.Aby uzyskać więcej informacji o lvalues i rvalues, zobacz Lvalues i Rvalues.Aby uzyskać więcej informacji o odwołaniach lvalue zobacz Odwołanie lValue; niewłaściwy deklarator: &.

W poniższych sekcjach opisano, jak rvalue odniesienia wsparcia realizacji przenieść semantykę i perfect przesyłania.

Przenieść semantykę

Rvalue odniesienia wsparcia realizacji przenieść semantykę, można znacznie zwiększyć wydajność aplikacji.Przenieś semantykę umożliwia pisanie kodu przenosi zasobów (takich jak pamięć przydzielany dynamicznie) z jednego obiektu do innego.Przenieś semantykę działa, ponieważ umożliwia zasobów do przeniesienia z tymczasowych obiektów, które nie wymienione gdzie indziej w programie.

Aby zaimplementować semantykę przenoszenia, zwykle zapewniają przenieść konstruktora, i opcjonalnie operatorem przypisania move (operator=), do swojej klasy.Operacje kopiowania i przypisania źródeł, których rvalues są następnie automatycznie korzystać z przenieść semantyki.W odróżnieniu od domyślnego konstruktora kopii kompilator nie udostępnia domyślnego konstruktora przenoszenia.Aby uzyskać więcej informacji o zapisu konstruktora przenoszenia i używać go w aplikacji, zobacz Jak: Konstruktor Przenieś zapisu.

Można również przeciążenie zwykłe funkcje i operatory, aby skorzystać z Przenieś semantykę.Visual C++ 2010Przenieś semantykę wprowadza się do biblioteki szablon standardowy (STL).Na przykład string klasy implementuje operacje semantykę przenoszenia.Rozważmy następujący przykład, który łączy kilka ciągów i drukuje wynik:

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

Przed Visual C++ 2010, każde wywołanie operator+ przydziela i zwraca nowe tymczasowe string (rvalue) obiektu.operator+nie można dołączyć jeden ciąg do drugiej, ponieważ nie wiadomo, czy ciągi źródła są lvalues lub rvalues.Jeśli ciągi źródła są zarówno lvalues, mogą odwoływać się gdzie indziej w programie i dlatego nie musi być modyfikowane.Przy użyciu odwołań rvalue operator+ można zmodyfikować, aby podjąć rvalues, którego nie można odwoływać się gdzie indziej w programie.Dlatego operator+ jeden ciąg teraz można dołączyć do innego.Może to znacznie zmniejszyć liczbę alokacji pamięci dynamicznej, string klasy należy wykonać.Więcej informacji o string klasy, zobacz basic_string Class.

Przenieś semantykę pomaga również po kompilator nie może zwrócić wartość optymalizacji (RVO) lub o nazwie zwraca wartość optymalizacji (NRVO).W tych przypadkach kompilator wywołuje konstruktor Przenieś, jeśli typ definiuje ją.Aby uzyskać więcej informacji o nazwie optymalizacji wartości, zobacz o nazwie zwrotu optymalizacji wartość w Visual C++ 2005.

Aby lepiej zrozumieć semantykę przenoszenia, należy rozważyć przykład wstawienie elementu do vector obiektu.Jeśli pojemność vector obiektu jest przekroczona, vector obiektu musi ponownie przydzielić pamięci dla jego elementy, a następnie skopiuj każdy element do innej lokalizacji w pamięci, aby zwolnić miejsce dla wstawionego elementu.Podczas operacji wstawiania kopii elementu, tworzy nowy element, wywołuje Konstruktor kopiujący, aby skopiować dane z poprzedniego elementu do nowego elementu i następnie niszczy poprzedniego elementu.Przenieś semantykę umożliwia przenoszenie obiektów bezpośrednio, bez konieczności wykonywania alokacji pamięci kosztowne i operacje kopiowania.

Aby wykorzystać semantykę Przenieś w vector przykład, można napisać konstruktora Przenieś do przenoszenia danych z jednego obiektu do innego.

Aby uzyskać więcej informacji dotyczących wprowadzania przenoszenia semantykę STL w Visual C++ 2010, zobacz Biblioteka języka C++ wzorcowego.

Przekazywanie Perfect

Przekazywanie Perfect ogranicza potrzebę przeciążonej funkcji i pomaga uniknąć problemu przesyłania dalej.Problem przesyłania może wystąpić, gdy zapis rodzajowy funkcji, która zajmuje odwołania jako jej parametry i przekazuje go (lub przesyła dalej) te parametry do innej funkcji.Na przykład funkcja generic przyjmuje parametr typu const T&, a następnie wywoływana funkcja nie może zmodyfikować wartość tego parametru.Jeśli funkcja generic przyjmuje parametr typu T&, a następnie nie można wywołać funkcji przy użyciu rvalue (na przykład tymczasowy obiekt lub literałem liczby całkowitej).

Zwykle, aby rozwiązać ten problem, należy podać przeciążony wersje funkcji rodzajowy, które zarówno T& i const T& dla każdego z jego parametry.W rezultacie liczba funkcji zastąpionej wykładniczo zwiększa się liczba parametrów.Rvalue odniesienia umożliwiają zapisu jedną wersję funkcji, która akceptuje dowolnego argumenty i przekazuje je do innej funkcji, jak gdyby była wywołana bezpośrednio inne funkcje.

Rozważmy następujący przykład, która deklaruje cztery typy W, X, Y, i Z.Konstruktora dla każdego typu ma inną kombinację const i non-const lvalue odwołania jako jego parametry.

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

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

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

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

Załóżmy, że zapis rodzajowy funkcji, która generuje obiektów.Poniższy przykład przedstawia sposób zapisu tej funkcji:

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

W poniższym przykładzie przedstawiono ważne wywołanie factory funkcji:

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

Jednak poniższy przykład zawiera ważne wywołanie factory działać, ponieważ factory ma lvalue odwołań, które można modyfikować jego parametry, ale nazywa się przy użyciu rvalues:

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

Zwykle, aby rozwiązać ten problem, należy utworzyć przeciążony wersję factory funkcji dla każdej kombinacji A& i const A& parametry.Rvalue odniesienia umożliwiają zapisu jedną wersję factory działać jak pokazano w następującym przykładzie:

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

W tym przykładzie użyto odniesienia rvalue jako parametry do factory funkcji.Celem std::forward funkcja jest do przekazania parametrów funkcji fabryki do konstruktora klasy szablonu.

W poniższym przykładzie main używająca zweryfikowanej factory funkcja tworzenia wystąpień W, X, Y, i Z klasy.Poprawione factory funkcji przekazuje jego parametry (lvalues lub rvalues) do konstruktora klasy właściwe.

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

Dodatkowe właściwości odwołania Rvalue

Mogą przeciążać funkcji odwołanie lvalue i rvalue.

Przeciążając funkcję do podjęcia const lvalue odwołanie lub odwołanie rvalue, można napisać kod, który rozróżnia nie można modyfikować obiektów (lvalues) i tymczasowe można modyfikować wartości (rvalues).Obiekt można przekazać do funkcji, która ma odwołanie rvalue, chyba, że obiekt jest oznaczony jako const.W poniższym przykładzie funkcja f, który jest przeciążony podjęcie odwołanie lvalue i rvalue.main Działać wywołania f z lvalues i 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());
}

Ten przykład generuje następujące wyniki:

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

W tym przykładzie pierwsze wywołanie f przekazuje zmienna lokalna (lvalue) jako argumentu.Drugie wywołanie f przekazuje tymczasowy obiekt jako argumentu.Ponieważ tymczasowy obiekt nie może odwoływać się gdzie indziej w programie, wywołanie wiąże przeciążony wersję f , przyjmuje odwołanie rvalue jest wolna zmodyfikować obiekt.

Kompilator traktuje odwołanie nazwany rvalue, jako lvalue i nienazwane rvalue jako rvalue.

Podczas pisania ma odwołanie rvalue jako parametr funkcji ten parametr jest traktowany jako lvalue w treści funkcji.Kompilator traktuje odwołanie do nazwanego rvalue jako lvalue, ponieważ nazwanego obiektu można się odwoływać za kilka części programu; byłoby niebezpieczne umożliwić wielu części programu, modyfikowanie lub usuwanie zasobów z tego obiektu.Na przykład wiele części programu próby przeniesienia zasoby z tego samego obiektu, tylko pierwsza część zostanie pomyślnie transfer zasobu.

W poniższym przykładzie funkcja g, który jest przeciążony podjęcie odwołanie lvalue i rvalue.Funkcja f przyjmuje odwołanie rvalue jako parametr (odwołanie do nazwanego rvalue) i zwraca odwołanie do rvalue (rvalue nienazwane odwołanie).W wywołaniu g z f, rozdzielczość przeciążenie wybiera wersję g , przyjmuje odwołanie lvalue, ponieważ treści f traktuje jako lvalue jej parametr.W wywołaniu g z main, rozdzielczość przeciążenie wybiera wersję g , przyjmuje odwołanie rvalue, ponieważ f zwraca odwołanie do 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()));
}

Ten przykład generuje następujące wyniki:

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

W tym przykładzie main funkcja przekazuje rvalue do f.Treści f traktuje jako lvalue jego nazwany parametr.Wywołanie z f do g wiąże parametr odwołanie lvalue (pierwsza wersja przeciążony g).

  • Można rzutować lvalue odniesienia rvalue.

STL std::move funkcja umożliwia konwertowanie obiektu rvalue odwołanie do tego obiektu.Alternatywnie, można użyć static_cast słowa kluczowego do oddania lvalue odniesienia rvalue, jak pokazano w następującym przykładzie:

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

Ten przykład generuje następujące wyniki:

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

 

Szablony funkcji wywnioskowanie ich typy argumentów szablonu, a następnie użyj odwołania zwijanie reguł.

Często zapisać szablon funkcji, który przekazuje się (lub przesyła dalej) jego parametry do innej funkcji.Jest zrozumieć, jak działa szablon typu potrącenia funkcji szablonów, które rvalue odwołania.

Jeśli argument funkcji jest rvalue, kompilator deduces argument odniesienie rvalue.Na przykład przekazać rvalue odwołanie do obiektu typu X funkcji szablonu, która ma typ T&& jako jej parametr szablonu odliczenie argumentu deduces T za X.Dlatego parametr ma typ X&&.Jeśli argument funkcji lvalue lub const lvalue, kompilator deduces jego typ za odwołanie lvalue lub const lvalue odwołanie tego typu.

W poniższym przykładzie deklaruje strukturę szablonów i następnie specjalizuje się dla różnych typów odwołań.print_type_and_value Funkcja przyjmuje odwołanie rvalue jako parametr i przekazuje go do odpowiedniej wersji wyspecjalizowanych S::print metody.main Funkcji ilustruje różne sposoby call S::print metody.

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

Ten przykład generuje następujące wyniki:

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

Aby rozwiązać każde wywołanie print_type_and_value funkcji kompilator najpierw wykonuje szablonu odliczenie argumentu.Następnie kompilator stosuje zwijanie zasady podczas jej substytuty argumenty typów parametrów szablonu wyprowadzonych odniesienia.Na przykład, przekazując zmiennej lokalnej s1 do print_type_and_value funkcji powoduje, że kompilator produkują podpisu następujących funkcji:

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

Kompilator używa odniesienia zwijanie reguły zmniejszenie podpis do następujących:

print_type_and_value<string&>(string& t)

Ta wersja programu print_type_and_value funkcja następnie przesyła jej parametr do poprawnej wersji wyspecjalizowanych S::print metody.

W poniższej tabeli zestawiono odniesienia zwijanie reguły szablonu argument Typ potrącenia:

Typu expanded

Typ zwinięty

T& &

T&

T& &&

T&

T&& &

T&

T&& &&

T&&

Potrącenie argumentu szablon jest ważnym elementem wykonawczych perfect przesyłania dalej.W sekcji Perfect dalej, przedstawiony wcześniej w tym temacie opisano perfect przekazywania bardziej szczegółowo.

Podsumowanie

Rvalue odniesienia odróżnić lvalues od rvalues.Pomagają zwiększyć wydajność aplikacji, eliminując potrzebę alokacji pamięci niepotrzebne i operacje kopiowania.Umożliwiają one również pisać jedną wersję funkcji, która akceptuje dowolnego argumenty i przekazuje je do innej funkcji, jak gdyby była wywołana bezpośrednio inne funkcje.

Zobacz też

Zadania

Jak: Konstruktor Przenieś zapisu

Informacje

Wyrażenia z operatory jednoargumentowe

Odwołanie lValue; niewłaściwy deklarator: &

Lvalues i Rvalues

move

forward

Inne zasoby

Biblioteka języka C++ wzorcowego