C++本來只有左值和右值,但是為了能充分利用右值,減少記憶體的分配,從而引入了將亡值。左值可以透過std::move()轉換成將亡值,右值也可以透過std::move()或者隱式型別轉換變為將亡值。將亡值僅僅是個標記,表示該表示式所持有的資源,可以被偷取。
如何打印表達式的型別資訊?
template
在C++中如何得到一個表示式的值型別?
int a = 10; // decltype可以推匯出一個表示式的型別 std::cout << “The type of a : ” << type_to_string
輸出:
// a的型別The type of a : int// a的值型別The value type of a : int&// std::move(a)的值型別The value type of a : int&&// 10的型別The type of 10 : int// 10的值型別The value type of 10 : int
從上面這幾個例子可以看出,
decltype((*))
推匯出的值型別,分為三類:
&&作為限定符的將亡值(xvalue)
&作為限定符的左值(lvalue)
無限定符的純右值(prvalue)
從而,可以透過std::is_lvalue_reference_v和std::is_rvalue_reference_v這兩個模板類來判斷左值和將亡值,不屬於它們中任意一個的就是純右值。 程式碼如下:
template
使用時傳入的模板引數是decltype((a)),這樣的形式。
注意
:上述concept只能用於判定值型別,無法實際在模板中使用,因為模板中傳入的是型別引數。
再定義一個宏:
#define value_type(value) \ do \ { \ std::cout << #value “ is lvalue : ” \ << is_lvalue
使用
std::cout << std::boolalpha; value_type(10); int a = 10; std::cout << “===========================” << std::endl; value_type(a); std::cout << “===========================” << std::endl; value_type(std::move(a));
輸出:
10 is lvalue : false10 is xvalue : false10 is prvalue : true10 is rvalue : true10 is glvalue : false===========================a is lvalue : truea is xvalue : falsea is prvalue : falsea is rvalue : falsea is glvalue : true===========================std::move(a) is lvalue : falsestd::move(a) is xvalue : truestd::move(a) is prvalue : falsestd::move(a) is rvalue : truestd::move(a) is glvalue : true
10是個純右值,同時也屬於右值;a是左值,同時也屬於glvalue;std::move(a)是一個將亡值,所以他也屬於rvalue和glvalue。
為什麼上面的例子要使用std::move(a),而不直接使用一個右值引用型別的變數呢?下面來看下,一個右值引用型別的變數的型別和值型別。
int&& b = 20; std::cout << “The type of b : ” << type_to_string
輸出:
The type of b : int&&b is lvalue : trueb is xvalue : falseb is prvalue : falseb is rvalue : falseb is glvalue : true
從上面的輸出可以看出,雖然b的型別為右值引用,但他卻是個左值。int&& b = 20 這一表達式,實際上是先為20在棧上分配一塊4位元組的記憶體,再將20存到這片記憶體中,再將記憶體地址賦值給b。
mov eax, 20mov DWORD PTR [rbp-12], eaxlea rax, [rbp-12]mov QWORD PTR [rbp-8], raxrbp 棧底地址rsp 棧頂地址dword 雙字 就是四個位元組ptr pointer縮寫 即指標[]裡的資料是一個地址值,這個地址指向一個雙字型資料比如mov DWORD PTR [rbp-12], eax 把記憶體地址rbp-12中的雙字型(32位)資料賦給eax lea load effective address 比如:lea rax, [rbp-12] 將rbp-12這個地址賦值給rax
表示式值型別的具體分類
lvalue
struct Person{ int age; static int weight;};int& f() { int* a = new int{10}; return *a;}void g() {}int main(){ std::cout << std::boolalpha; // 1。 普通變數 int a = 10; std::cout << “a is lvalue : ” << is_lvalue
prvalue
std::cout << std::boolalpha; // 1。 除字串字面量以外的其它字面量 std::cout << “10 is prvalue : ” << is_prvalue
xvalue
struct Person{ int age; static int weight;};int& f() { int* a = new int{10}; return *a;}void g() {}int&& h(){ return 10;}int main(){ std::cout << std::boolalpha; // 1。 std::move int a = 10; std::cout << “std::move(10) is xvalue : ” << is_xvalue
std::move中的一個坑
struct Person{ int age; static int weight; std::string& name;};int main(){ std::cout << std::boolalpha; std::string name = “xiaoming”; Person p{10, name}; std::cout << “===============” << std::endl; value_type(std::move(p)。age); std::cout << “===============” << std::endl; value_type(std::move(p)。weight); std::cout << “===============” << std::endl; value_type(std::move(p)。name);}===============std::move(p)。age is lvalue : falsestd::move(p)。age is xvalue : truestd::move(p)。age is prvalue : falsestd::move(p)。age is rvalue : truestd::move(p)。age is glvalue : true===============std::move(p)。weight is lvalue : truestd::move(p)。weight is xvalue : falsestd::move(p)。weight is prvalue : falsestd::move(p)。weight is rvalue : falsestd::move(p)。weight is glvalue : true===============std::move(p)。name is lvalue : truestd::move(p)。name is xvalue : falsestd::move(p)。name is prvalue : falsestd::move(p)。name is rvalue : falsestd::move(p)。name is glvalue : true
從上述輸出,可以看出std::move(p)。age是個xvalue; std::move(p)。weight和std::move(p)。name都是lvalue。也就是說,weight和name都不是Person類的一部分,Person類對於這兩個成員變數沒有所有權。應改為std::move(p。weight)和std::move(p。name),如果希望改變它們的值型別的話。
std::cout << std::boolalpha; std::string name = “xiaoming”; Person p{10, name}; std::cout << “===============” << std::endl; value_type(std::move(p。age)); std::cout << “===============” << std::endl; value_type(std::move(p。weight)); std::cout << “===============” << std::endl; value_type(std::move(p。name));===============std::move(p。age) is lvalue : falsestd::move(p。age) is xvalue : truestd::move(p。age) is prvalue : falsestd::move(p。age) is rvalue : truestd::move(p。age) is glvalue : true===============std::move(p。weight) is lvalue : falsestd::move(p。weight) is xvalue : truestd::move(p。weight) is prvalue : falsestd::move(p。weight) is rvalue : truestd::move(p。weight) is glvalue : true===============std::move(p。name) is lvalue : falsestd::move(p。name) is xvalue : truestd::move(p。name) is prvalue : falsestd::move(p。name) is rvalue : truestd::move(p。name) is glvalue : true
這樣,三個成員變數都變成了xvalue。