共用方式為


Rvalue 參考宣告子: &&

保留右值運算式的參考。

語法

rvalue-reference-type-id
type-specifier-seq&&attribute-specifier-seqopt optptr-abstract-declarator

備註

右值參考可讓您區分左值與右值。 Lvalue 參考和右值參考在語法上和語意上相似,但會遵循稍微不同的規則。 如需左值和右值的詳細資訊,請參閱 左值和右值。 如需左值參考的詳細資訊,請參閱 Lvalue 參考宣告子: 和

下列各節說明右值參考如何支持移動語意的實作完美的轉送

移動語意

右值參考支持行動語意實作,這可大幅提升應用程式的效能。 移動語意可讓您撰寫將資源從一個物件轉移到另一個物件 (例如動態配置記憶體) 的程式碼。 移動語意的運作方式是因為它能夠從暫存物件傳輸資源:無法在程式中其他地方參考的資源。

若要實作行動語意,您通常會提供 行動建構函式, 以及選擇性地將移動指派運算子 (operator=) 提供給類別。 然後,來源為右值的複製和指派作業會自動利用移動語意。 不同於預設複製建構函式,編譯程式不會提供預設移動建構函式。 如需如何撰寫和使用移動建構函式的詳細資訊,請參閱 移動建構函式和移動指派運算符

您也可以多載一般函式和運算子,以使用移動語意。 Visual Studio 2010 引進將語意移至 C++ 標準連結庫。 例如,類別 string 會實作使用行動語意的作業。 請考慮下列串連數個字串並列印結果的範例:

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

在 Visual Studio 2010 之前,每個呼叫都會 operator+ 配置並傳回新的暫存 string 物件(右值)。 operator+ 無法將一個字串附加至另一個字串,因為它不知道來源字串是否為左值或右值。 如果來源字串都是左值,則程式中的其他位置可能會參考它們,因此不得修改。 您可以使用 rvalue 參考來修改 operator+ 以取得右值,而此參考無法在程式中其他地方參考。 透過這項變更, operator+ 現在可以將一個字串附加至另一個字串。 此變更可大幅減少類別必須執行的易失記憶體配置 string 數目。 如需 類別 string 的詳細資訊,請參閱 basic_string 類別

當編譯程式無法使用傳回值優化 (RVO) 或具名傳回值優化 (NRVO) 時,移動語意也會有所説明。 在這些情況下,如果類型定義了移動建構函式,則編譯器會加以呼叫。

若要進一步了解移動語意,請參考將項目插入至 vector 物件的範例。 如果超過物件的容量 vectorvector 對象必須為其元素重新配置足夠的記憶體,然後將每個元素複製到另一個記憶體位置,以騰出空間供插入的專案使用。 當插入作業複製專案時,它會先建立新的專案。 然後,它會呼叫複製建構函式,將數據從上一個專案複製到新元素。 最後,它會終結前一個專案。 移動語意可讓您直接移動物件,而不需要進行昂貴的記憶體配置和複製作業。

若要利用 vector 範例中的移動語意,您可以寫入移動建構函式,將資料從某個物件移動至另一個。

如需在 Visual Studio 2010 中將語意移至 C++ 標準連結庫的詳細資訊,請參閱 C++ 標準連結庫

完美轉寄

完美轉送可減少對多載函式的需求,有助於避免轉送問題。 當您 撰寫採用參考做為其參數的泛型函式時,可能會發生轉送問題 。 如果它會將這些參數傳遞至另一個函式,例如,如果它接受 類型的const T&參數,則呼叫的函式無法修改該參數的值。 如果泛型函式採用 類型的 T&參數,則無法使用右值呼叫函式(例如暫存物件或整數常值)。

若要解決這個問題,您通常必須提供每個參數採用 T&const T& 的泛型函式多載版本。 因此,多載函式數目會隨著參數數目以指數方式增加。 右值參考可讓您撰寫接受任意自變數的函式版本。 然後,該函式可以將它們轉送至另一個函式,就像直接呼叫另一個函式一樣。

請考慮宣告 WXYZ 這四種類型的下列範例。 每個類型的建構函式會採用不同組合 const 和非const 左值參考作為其參數。

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

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

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

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

假設您要撰寫產生物件的泛型函式。 下列範例示範撰寫此函式的其中一種方法:

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

下列範例示範對 factory 函式的有效呼叫:

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

不過,下列範例不包含對 函式的有效呼叫 factory 。 這是因為 factory 採用可修改為其參數的左值參考,但會使用右值來呼叫:

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

若要解決這個問題,您通常必須為每個 factoryA& 參數的組合建立 const A& 函式的多載版本。 右值參考可讓您撰寫 factory 函式的一個版本,如下列範例所示:

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

這個範例使用右值參考做為 factory 函式的參數。 函式 std::forward 的目的是將 Factory 函式的參數轉送至樣板類別的建構函式。

下列範例示範 main 函式,這個函式會使用修改的 factory 函式來建立 WXYZ 類別的執行個體。 修改的 factory 函式會將其參數 (左值或右值) 轉送至適當的類別建構函式。

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

右值參考的屬性

您可以多載函式來取得左值參考和右值參考。

藉由多載函式來取得 const 左值參考或右值參考,您可以撰寫程式代碼來區分不可修改的物件 (lvalues) 和可修改的暫存值 (rvalues)。 除非物件標示為 const,否則您可以將 對象傳遞至接受右值參考的函式。 下列範例說明多載之後可接受左值參考和右值參考的 f 函式。 main 函式會使用左值和右值呼叫 f

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

這個範例會產生下列輸出:

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

在此範例中,第一個對 f 的呼叫會傳遞區域變數 (左值) 做為其引數。 對 f 的第二個呼叫會傳遞暫存物件做為其引數。 由於暫存對象無法在程式的其他位置參考,因此呼叫會系結至採用右值參考的 多載版本 f ,這可以自由修改物件。

編譯程式會將具名右值參考視為左值,並將未命名的右值參考視為右值。

採用右值參考做為參數的函式會將參數視為函式主體中的左值。 編譯程式會將具名右值參考視為左值。 這是因為程式有數個部分可以參考具名物件。 允許程式的多個部分修改或移除該對象的資源,這很危險。 例如,如果程式的多個部分嘗試從相同物件傳輸資源,則只有第一個傳輸成功。

下列範例說明多載之後可接受左值參考和右值參考的 g 函式。 f 函式接受右值參考做為其參數 (具名右值參考),並傳回右值參考 (未具名右值參考)。 從 gf 的呼叫中,多載解析會選取採用左值參考的 g 版本,因為 f 主體會將其參數視為左值。 從 gmain 的呼叫中,多載解析會選取採用右值參考的 g 版本,因為 f 會傳回右值參考。

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

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

這個範例會產生下列輸出:

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

在這裡範例中,函式會將 main 右值傳遞至 ff 主體會將其具名參數視為左值。 從 fg 的呼叫會將參數繫結至左值參考 (g 的第一個多載版本)。

  • 您可以將左值轉換成右值參考。

C++ 標準連結庫 std::move 函式可讓您將物件轉換成該物件的右值參考。 您也可以使用 static_cast 關鍵字將左值轉換成右值參考,如下列範例所示:

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

這個範例會產生下列輸出:

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

函式範本會推算其範本自變數類型,然後使用參考折疊規則。

將參數傳遞至另一個函式的函式範本是常見的模式。 請務必瞭解範本類型推算如何適用於採用右值參考的函式範本。

如果函式引數為右值,編譯器會推算引數為右值參考。 例如,假設您將類型 X 物件的右值參考傳遞至採用 類型 T&& 做為其參數的函式範本。 樣板自變數推算為 TX,因此參數的類型為 X&&。 如果函式自變數是左值或 const 左值,編譯程式會將其類型推算為該類型的左值參考或 const 左值參考。

下列範例宣告某個結構樣板,然後針對各種參考類型特製化此樣板。 print_type_and_value 函式接受右值參考做為其參數,並將其轉送給 S::print 方法的適當特製化版本。 main 函式示範各種呼叫 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());
}

這個範例會產生下列輸出:

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

若要解析函式的每個呼叫 print_type_and_value ,編譯程式會先執行樣板自變數推算。 編譯程式接著會在將參數類型取代為推斷的範本自變數時,套用參考折疊規則。 例如,將區域變數 s1 傳遞至 print_type_and_value 函式會造成編譯器產生下列函式簽章:

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

編譯程式會使用參考折疊規則來減少簽章:

print_type_and_value<string&>(string& t)

print_type_and_value 函式的這個版本接著會將其參數轉送給 S::print 方法的正確特製化版本。

下表摘要說明樣板引數類型推算的參考摺疊規則:

展開的類型 摺疊的類型
T& & T&
T& && T&
T&& & T&
T&& && T&&

樣板引數推算是實作完美轉送的要素。 [完美轉寄] 區段會更詳細地描述完美的轉送。

摘要

右值參考會區分左值與右值。 為了改善應用程式的效能,它們可以消除不必要的記憶體配置和複製作業的需求。 它們也可讓您撰寫可接受任意自變數的函式。 該函式可以將它們轉送至另一個函式,就像直接呼叫另一個函式一樣。

另請參閱

具有一元運算子的表達式
Lvalue 參考宣告子: &
左值和右值
移動建構函式和移動指派運算子 (C++)
C++ 標準程式庫