右值參考的宣告子: & &
保留右值運算式的參考。
type-id && cast-expression
備註
右值參考可讓您使用右值差異左值。 Lvalue 參考和右值參考的語法和語意很類似,但是,某些遵守不同的規則。 如需和 rvalues 左值的詳細資訊,請參閱 值和 Rvalues。 如需左值參考的詳細資訊,請參閱 左值使用參考宣告子: &。
下列章節描述 rvalue 參考如何支援 移動語意 和 程式庫中的實作。
移動語意
右值參考支援 移動語意的實作,而大量增加應用程式的效能。 移動語意可讓您撰寫傳輸資源的程式碼 (例如動態配置的記憶體) 從物件傳輸到另一個。 移動語意運作,因為它可以讓資源會將程式無法在其他地方參考的暫存物件傳輸。
若要實作"移動語意,通常會提供 捲動建構函式 和選擇性地移動指派運算子 (operator=),讓您的類別。 來源會自動為 rvalues 然後複本和指派作業使用移動語意。 不同於預設複製建構函式,所以編譯器不提供預設移動建構函式。 如需如何撰寫移動建構函式以及如何使用它的詳細資訊會儲存在您的應用程式,請參閱 How to: 撰寫移動建構函式。
您也可以多載泛型函式和運算子使用移動語意。 Visual C++ 2010 引入移動語意到標準樣板程式庫 (STL)。 例如, 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 C++ 2010前,會 operator+ 的每個呼叫上配置並傳回新的暫存物件 string (rvalue)。 operator+ 無法附加至另一個字串,因為它不知道來源字串是否為左值或 rvalues。 如果來源字串都是左值,它們在程式可能位於其他地方參考也無法修改。 您可以使用 rvalue 參考,可以修改 operator+ 採用 rvalues,不能在程式中的其他位置參考。 因此, operator+ 現在可將某一個字串附加至另一個。 這樣可以大幅降低 string 類別必須執行動態記憶體配置的數目。 如需 string 類別的詳細資訊,請參閱 basic_string Class。
而使編譯器無法使用傳回值"最佳化 (RVO) 或具名傳回值最佳化 (NRVO) 時,移動語意也很有用。 在這些情況下,則為,如果型別定義它,則編譯器會告訴移動建構函式。 如需具名傳回值最佳化的詳細資訊, Visual C++ 2005 中的具名傳回值最佳化請參閱
進一步了解移動語意,請考慮插入項目的範例。 vector 物件。 如果 vector 物件的容量已超過, vector 物件必須重新指派其元素的記憶體然後複製每一個項目至另一個記憶體位置出空間給已插入的項目。 當插入作業複製項目時,會建立一個新項目,呼叫複製建構函式複製與前一個項目的資料加入至新的項目,再終結前一個項目。 移動語意可以讓您移動物件,而不需直接執行昂貴的記憶體配置和複製作業。
若要使用在 vector 範例中移動語意,您可從物件的移動資料寫入移動建構函式加入至另一個。
如需移動語意的呈現方式的詳細資訊加入至 Visual C++ 2010STL 的位置,請參閱 標準 C++ 程式庫參考。
完美轉送
程式庫中的降低多載函式的需求,並協助避免轉送問題。 向前問題 ,可能會發生在撰寫泛型函式時做為參數和其傳入做為參考 (或傳遞) 這些參數傳遞到另一個函式。 例如,在中,如果泛型函式接受型別 const T&參數,然後呼叫的函式不能修改該參數的值。 如果泛型函式接受型別 T&參數,則函式不能呼叫使用右值 (例如暫存物件或整數常值)。
通常,解決這個問題,您必須提供可接受 T& 和 const T& 它的每個參數的泛型函式的多載版本。 因此,多載的函式數目會以指數方式將具有參數的數目。 右值參考可讓您撰寫接受選擇性引數並將它們寫入其他函式的版本,就如同其他函式直接呼叫。
考慮宣告四個型別、、、 WXY和 Z的範例。 每個型別的建構函式會接受 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 接受使用 rvalues,是可以修改的做為其參數的左值的參考,但更重要的是,它會呼叫:
Z* pz = factory<Z>(2, 2);
通常,解決這個問題,您必須建立 factory 函式的多載版本 A& 和 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 參數的函式,這個範例使用 rvalue 參考。 std::forward 函式的目的是要轉送 Factory 函式的參數至樣板類別的建構函式。
下列範例顯示使用修改的 factory 函式建立 W、 X、 Y和 Z 類別執行個體的 main 函式。 修改過的 factory 函式順向其參數 (左值或 rvalues) 到適當的類別建構函式。
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 左值參考或右值參考,您可以撰寫區別不可修改的物件的程式碼 (左值) 和可修改的暫存值 (rvalues) 之間。 您可以傳遞給採用 rvalue 參考的函式的物件,除非物件標記為 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 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());
}
這個範例會產生下列輸出:
In f(const MemoryBlock&). This version cannot modify the parameter.
In f(MemoryBlock&&). This version can modify the parameter.
在此範例中,會 f 的第一個呼叫會透過區域變數 (左值) 做為其引數。 為 f 的第二個呼叫會將暫存物件做為其引數。 由於暫存物件在程式無法在其他地方參考,這個呼叫繫結至採用 rvalue 參考,可以自由修改物件 f 的多載版本。
編譯器會將具名 rvalue 參考做為左值和未命名的右值參考當做右值。
當您撰寫採用 rvalue 參考做為其參數的函式時,會將參數視為在函式主體的左值。 因為已命名的物件,可供程式的幾個部分參考編譯器將具名 rvalue 參考做為左值;允許程式的多個部門從該物件修改或移除資源是非常危險的。 例如,在中,如果程式有多個部門嘗試從相同物件傳輸資源,,則只有第一個部分會成功傳輸資源。
下列範例顯示了函式, g多載使用左值參考和右值參考。 函式 f 採用 rvalue 參考做為它的參數 (具名 rvalue 參考) 和傳回 rvalue 參考 (未命名的右值參考)。 在 [ g 的呼叫會從 f,多載解析選擇採用左值參考 g 版本,因為 f 主體會將它的參數做為左值。 在 [ g 的呼叫會從 main,多載解析選擇採用 rvalue 參考 g 版本,因為 f 傳回 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()));
}
這個範例會產生下列輸出:
In g(const MemoryBlock&).
In g(MemoryBlock&&).
在此範例中, main 函式傳遞至 f右值。 f 主體會將它的具名參數做為左值。 從 f 呼叫 g 將參數繫結至左值參考 ( g第一個多載版本)。
- 您可以將它轉換為左值 rvalue 參考。
針對 std::move 函式讓您能夠轉換為 rvalue 參考為該物件。 或者,如下列範例所示,您可以使用 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&&).
函式樣板推算其樣板引數型別會使用摺疊規則的參考。
一般會將傳遞的函式樣板 (或傳遞) 其參數傳遞到另一個函式。 瞭解樣板型別推算如何用於採用 rvalue 參考的函式樣板運作。
如果函式引數為右值,編譯器會推斷引數是 rvalue 參考。 例如,在中,如果您傳遞至型別 X 物件的右值參考到採用型別 T&& 做為其參數的樣板函式,樣板引數推算推算 T 是 X。 因此,參數具有型別 X&&。 如果函式引數是左值或 const 左值,編譯器會推斷其型別是左值參考或 const 該型別左值參考。
下列範例會宣告一個架構範本然後特製化此物件的各種參考型別的。 print_type_and_value 函式採用 rvalue 參考做為它的參數並將它轉送至 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&& |
樣板引數推算為實作程式庫中的項目。 部分程式庫中的,在這個主題會描述之前,提出更詳細地轉寄。
摘要
右值參考與 rvalues 差異左值。 它們可以協助您透過不必對不必要的記憶體配置和複製作業需求改善應用程式效能。 也可以讓您撰寫接受選擇性引數並將它們寫入其他函式的版本,就如同其他函式直接呼叫。